CaiWuXiTong/tests/Unit/EcommerceFinance/StoreAccountMirrorRepositoryTest.php
renshen 473b6f4ebb 修复流程审批状态取不到:OA 流程表改用 jiansun 的实时副本
原来取的是中台库 dbo.flow_flow,那份数据停在 2026-07-16,7 月之后的申请
(比如 shp20260716165004772 京喜-清木居工厂店 关店流程)审批状态全是空的,
而工作平台显示「流程中」。

jiansun.dbo.flow_flow 是实时表(今天还在更新,10543 行),同名单据里
flowStatus=1、当前审批人=山时。已登记并完成首次全量 + 建 flowId 索引;
中台那份旧副本改为停止同步(数据保留)。

流程统计页与店铺账号密码的「流程类型 / 审批状态 / 流程状态」两处取值都跟着换成实时表。
2026-10-09 10:25:43 +08:00

239 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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\StoreAccountMirrorRepository;
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
use Tests\TestCase;
class StoreAccountMirrorRepositoryTest extends TestCase
{
private StoreAccountMirrorRepository $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' => [
StoreAccountMirrorRepository::DATASET => [
'source_key' => 'database_4',
'schema' => 'external_store_accounts',
'object' => 'accounts_v1',
'target' => 'store_accounts',
'keys' => ['id'],
// 模拟 business-mirror:follow-upstream-columns 记录下来的跟随字段。
'followed_columns' => ['platform'],
],
'jiansun.dbo.fys_shopInfo_flow' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'fys_shopInfo_flow',
'target' => 'shop_info_flow_form', 'keys' => ['Id'],
],
'jiansun.dbo.flow_flow' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'flow_flow',
'target' => 'oa_flow_flow', 'keys' => ['Id'],
],
],
]);
DB::purge('business_mirror');
Schema::connection('business_mirror')->create('business_mirror_table_states', function (Blueprint $table): void {
$table->string('dataset_key')->primary();
$table->dateTime('initial_completed_at')->nullable();
});
Schema::connection('business_mirror')->create('store_accounts', function (Blueprint $table): void {
$table->string('id')->primary();
foreach (['storeName', 'operationOwner', 'operationDepartmentHead', 'operationDirector', 'profitCenter', 'accountType', 'account', 'boundPhone', 'verificationContact', 'accountGroupName', 'createdAt', 'updatedAt'] as $column) {
$table->string($column)->nullable();
}
$table->boolean('passwordConfigured')->nullable();
// 自动跟随上游补进来的字段。
$table->string('platform')->nullable();
});
// 授权状态 / 店铺状态来自这两张镜像表(按店铺名关联)。
Schema::connection('business_mirror')->create('ops_fys_shop_info', function (Blueprint $table): void {
$table->integer('Id');
$table->string('erpShopName')->nullable();
$table->integer('shopState')->nullable();
});
Schema::connection('business_mirror')->create('ops_jst_shop_info', function (Blueprint $table): void {
$table->integer('Id');
$table->string('shop_name')->nullable();
$table->dateTime('session_expired')->nullable();
});
// 店铺关店流程:表单(含 ERP 店铺名)+ OA 流程状态。
Schema::connection('business_mirror')->create('shop_info_flow_form', function (Blueprint $table): void {
$table->integer('Id');
$table->string('flowId')->nullable();
$table->integer('flowType')->nullable();
$table->string('erpShopName')->nullable();
$table->dateTime('applyTime')->nullable();
});
Schema::connection('business_mirror')->create('oa_flow_flow', function (Blueprint $table): void {
$table->integer('Id');
$table->string('flowId')->nullable();
$table->integer('flowStatus')->nullable();
});
DB::connection('business_mirror')->table('business_mirror_table_states')->insert([
'dataset_key' => StoreAccountMirrorRepository::DATASET,
'initial_completed_at' => '2026-09-04 10:00:00',
]);
$this->repository = new StoreAccountMirrorRepository(
new BusinessMirrorDatasetRegistry(),
new GenericBusinessMirrorRepository(app('db')),
);
}
public function test_it_maps_account_type_codes_to_business_labels(): void
{
foreach (['MAIN' => '主账号', 'SUB' => '子账号', '/' => null] as $type => $expected) {
DB::connection('business_mirror')->table('store_accounts')->insert([
'id' => 'account-' . md5($type),
'storeName' => ['MAIN' => 'B店铺', 'SUB' => 'A店铺', '/' => 'C店铺'][$type],
'accountType' => $type,
'account' => 'test-account',
'passwordConfigured' => $type === 'MAIN' ? true : ($type === 'SUB' ? false : null),
]);
}
$rows = collect($this->repository->paginate(1, 50)['data'])->keyBy('id');
$this->assertSame('主账号', $rows['account-' . md5('MAIN')]['account_type']);
$this->assertSame('子账号', $rows['account-' . md5('SUB')]['account_type']);
$this->assertNull($rows['account-' . md5('/')]['account_type']);
$this->assertTrue($rows['account-' . md5('MAIN')]['password_configured']);
$this->assertFalse($rows['account-' . md5('SUB')]['password_configured']);
$this->assertNull($rows['account-' . md5('/')]['password_configured']);
$sorted = $this->repository->paginate(1, 50, ['_sort' => 'store_name', '_direction' => 'asc']);
$this->assertSame(['A店铺', 'B店铺', 'C店铺'], array_column($sorted['data'], 'store_name'));
}
public function test_it_shows_the_latest_application_flow(): void
{
$db = DB::connection('business_mirror');
// 账号表用的就是带平台前缀的 ERP 店铺名(关店流程里可能写成下划线,需要归一)。
foreach (['拼多多-甲店' => '拼多多_甲店', '拼多多-乙店' => '拼多多-乙店', '拼多多-丙店' => '拼多多-丙店'] as $store => $erp) {
$db->table('store_accounts')->insert([
'id' => 'account-' . md5($store), 'storeName' => $store, 'account' => 'acct-' . $store,
]);
// 只有建店流程的表单也放进来,确认只取关店流程(flowType=10)。
$db->table('shop_info_flow_form')->insert([
'Id' => crc32('open-' . $store), 'flowId' => 'open-' . $store, 'flowType' => 8,
'erpShopName' => $erp, 'applyTime' => '2026-09-01 09:00:00',
]);
}
// 甲店/乙店:最近一单是关店流程(流程中 / 完成);丙店只有开店流程。
$db->table('shop_info_flow_form')->insert([
['Id' => 1, 'flowId' => 'close-jia', 'flowType' => 10, 'erpShopName' => '拼多多_甲店', 'applyTime' => '2026-10-01 09:00:00'],
['Id' => 2, 'flowId' => 'close-yi', 'flowType' => 10, 'erpShopName' => '拼多多-乙店', 'applyTime' => '2026-10-02 09:00:00'],
]);
$db->table('oa_flow_flow')->insert([
['Id' => 1, 'flowId' => 'close-jia', 'flowStatus' => 1],
['Id' => 2, 'flowId' => 'close-yi', 'flowStatus' => 3],
]);
$rows = collect($this->repository->paginate(1, 10)['data'])->keyBy('store_name');
$this->assertSame('店铺关店', $rows['拼多多-甲店']['flow_type']);
$this->assertSame('流程中', $rows['拼多多-甲店']['approval_status']);
$this->assertSame('店铺关店', $rows['拼多多-乙店']['flow_type']);
$this->assertSame('完成', $rows['拼多多-乙店']['approval_status']);
// 丙店最近一单是开店流程,没有对应 OA 状态。
$this->assertSame('店铺开店', $rows['拼多多-丙店']['flow_type']);
$this->assertSame('', $rows['拼多多-丙店']['approval_status']);
$filtered = $this->repository->paginate(1, 10, ['approval_status' => ['流程中']]);
$this->assertSame(['拼多多-甲店'], array_column($filtered['data'], 'store_name'));
$byType = $this->repository->paginate(1, 10, ['flow_type' => ['店铺门店']]);
$this->assertSame([], array_column($byType['data'], 'store_name'));
$closeOnly = $this->repository->paginate(1, 10, ['flow_type' => ['店铺关店']]);
$this->assertSame(2, $closeOnly['total']);
}
public function test_it_exposes_followed_upstream_columns_to_the_page(): void
{
DB::connection('business_mirror')->table('store_accounts')->insert([
'id' => 'account-followed',
'storeName' => 'A店铺',
'account' => 'acct-A',
'platform' => '拼多多',
]);
$this->assertSame(['platform'], $this->repository->followedColumns());
$this->assertSame([
['name' => 'store_name', 'label' => '店铺名称'],
['name' => 'platform', 'label' => 'platform', 'followed' => true],
], $this->repository->withFollowedFields([['name' => 'store_name', 'label' => '店铺名称']]));
$rows = collect($this->repository->paginate(1, 10)['data'])->keyBy('id');
$this->assertSame('拼多多', $rows['account-followed']['platform']);
}
public function test_it_attaches_authorization_and_store_status_and_filters_them(): void
{
$db = DB::connection('business_mirror');
foreach (['A店铺' => '主账号', 'B店铺' => '主账号', 'C店铺' => '子账号', 'D店铺' => '子账号'] as $store => $type) {
$db->table('store_accounts')->insert([
'id' => 'account-' . md5($store),
'storeName' => $store,
'accountType' => $type === '主账号' ? 'MAIN' : 'SUB',
'account' => 'acct-' . $store,
]);
}
// 店铺档案:A 正常、B 已关闭;聚水潭授权:A 未过期、B 已过期、C 无授权时间,
// D 两条记录都没有。
$db->table('ops_fys_shop_info')->insert([
['Id' => 1, 'erpShopName' => 'A店铺', 'shopState' => 0],
['Id' => 2, 'erpShopName' => 'B店铺', 'shopState' => 6],
]);
$db->table('ops_jst_shop_info')->insert([
['Id' => 1, 'shop_name' => 'A店铺', 'session_expired' => now()->addYear()->toDateTimeString()],
['Id' => 2, 'shop_name' => 'B店铺', 'session_expired' => now()->subDay()->toDateTimeString()],
['Id' => 3, 'shop_name' => 'C店铺', 'session_expired' => null],
]);
$rows = collect($this->repository->paginate(1, 50)['data'])->keyBy('store_name');
$this->assertSame('已授权', $rows['A店铺']['authorization_status']);
$this->assertSame('正常', $rows['A店铺']['store_status']);
$this->assertSame('已过期', $rows['B店铺']['authorization_status']);
$this->assertSame('已关闭', $rows['B店铺']['store_status']);
$this->assertSame('未授权', $rows['C店铺']['authorization_status']);
$this->assertSame('', $rows['C店铺']['store_status']);
$this->assertSame('', $rows['D店铺']['authorization_status']);
$this->assertSame('', $rows['D店铺']['store_status']);
$authorized = $this->repository->paginate(1, 50, ['authorization_status' => '已授权', 'store_status' => '正常']);
$this->assertSame(1, $authorized['total']);
$this->assertSame(['A店铺'], array_column($authorized['data'], 'store_name'));
$missing = $this->repository->paginate(1, 50, ['authorization_status' => '未授权']);
$this->assertSame(['C店铺'], array_column($missing['data'], 'store_name'));
// 多选:勾选多个状态时取并集(正常 + 已关闭)。
$multi = $this->repository->paginate(1, 50, ['store_status' => ['正常', '已关闭']]);
$this->assertSame(['A店铺', 'B店铺'], array_column($multi['data'], 'store_name'));
$multiAuth = $this->repository->paginate(1, 50, ['authorization_status' => ['已授权', '未授权']]);
$multiAuthNames = array_column($multiAuth['data'], 'store_name');
sort($multiAuthNames);
$this->assertSame(['A店铺', 'C店铺'], $multiAuthNames);
// 关联出来的列也能排序:正常 -> 已关闭 -> 无档案。
$sorted = $this->repository->paginate(1, 50, ['_sort' => 'store_status', '_direction' => 'asc']);
$this->assertSame(['A店铺', 'B店铺'], array_slice(array_column($sorted['data'], 'store_name'), 0, 2));
$sortedDesc = $this->repository->paginate(1, 50, ['_sort' => 'store_status', '_direction' => 'desc']);
$this->assertSame(['B店铺', 'A店铺'], array_slice(array_column($sortedDesc['data'], 'store_name'), 2));
}
}