diff --git a/config/business_navigation.php b/config/business_navigation.php index f4d23c95..59124738 100644 --- a/config/business_navigation.php +++ b/config/business_navigation.php @@ -351,7 +351,8 @@ $navigation = [ ['name' => 'account_group_name', 'label' => '账号分组名称'], ['name' => 'authorization_status', 'label' => '授权状态'], ['name' => 'store_status', 'label' => '店铺状态'], - ['name' => 'flow_status', 'label' => '流程状态'], + ['name' => 'flow_type', 'label' => '流程类型'], + ['name' => 'approval_status', 'label' => '审批状态'], ['name' => 'created_at', 'label' => '店铺创建时间', 'kind' => 'date'], ['name' => 'updated_at', 'label' => '店铺修改时间', 'kind' => 'date'], ], diff --git a/modules/EcommerceFinance/Repositories/StoreAccountMirrorRepository.php b/modules/EcommerceFinance/Repositories/StoreAccountMirrorRepository.php index 4fe92dbd..8be8d34a 100644 --- a/modules/EcommerceFinance/Repositories/StoreAccountMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/StoreAccountMirrorRepository.php @@ -27,16 +27,16 @@ class StoreAccountMirrorRepository /** 授权状态文案;顺序同时用于下拉和排序。 */ private const AUTHORIZATION_LABELS = ['已授权', '已过期', '未授权']; - /** 店铺关店流程(jst_shopInfo_flow.flowType=10)所在的数据集。 */ - private const CLOSE_FLOW_FORM_DATASET = 'jiansun.dbo.fys_shopInfo_flow'; - private const CLOSE_FLOW_OA_DATASET = 'js_data_middle_platform.dbo.flow_flow'; - private const CLOSE_FLOW_TYPE = 10; + /** 店铺申请流程(开店/变更/关店/主体注销自动)所在的数据集。 */ + private const FLOW_FORM_DATASET = 'jiansun.dbo.fys_shopInfo_flow'; + private const FLOW_OA_DATASET = 'js_data_middle_platform.dbo.flow_flow'; - /** 流程状态文案;顺序同时用于下拉和排序。 */ + /** 流程类型 / 审批状态文案;顺序同时用于下拉和排序。 */ + private const FLOW_TYPE_LABELS = ['店铺开店', '店铺变更', '店铺关店', '主体注销自动流程']; private const FLOW_STATUS_LABELS = ['流程中', '待审批', '完成', '拒绝', '撤回', '暂存', '待签署', '已签署']; /** 账号店铺名 -> 状态 的缓存键(两张档案表要全表读,缓存 10 分钟)。 */ - private const STATUS_CACHE_KEY = 'store-accounts.statuses:v2'; + private const STATUS_CACHE_KEY = 'store-accounts.statuses:v3'; public function __construct( private readonly BusinessMirrorDatasetRegistry $datasets, @@ -83,18 +83,21 @@ class StoreAccountMirrorRepository $statusFilters = [ 'store_status' => $this->filterValues($filters['store_status'] ?? ''), 'authorization_status' => $this->filterValues($filters['authorization_status'] ?? ''), - 'flow_status' => $this->filterValues($filters['flow_status'] ?? ''), + 'flow_type' => $this->filterValues($filters['flow_type'] ?? ''), + 'approval_status' => $this->filterValues($filters['approval_status'] ?? ''), ]; $statuses = ($statusFilters['store_status'] !== [] || $statusFilters['authorization_status'] !== [] - || $statusFilters['flow_status'] !== []) + || $statusFilters['flow_type'] !== [] + || $statusFilters['approval_status'] !== []) ? $this->statusesByStoreName() : null; if ($statuses !== null) { foreach ([ 'store_status' => 'store', 'authorization_status' => 'authorization', - 'flow_status' => 'flow', + 'flow_type' => 'flow_type', + 'approval_status' => 'flow', ] as $key => $bucket) { $expected = $statusFilters[$key]; if ($expected === []) { @@ -125,9 +128,11 @@ class StoreAccountMirrorRepository $statuses ??= $this->statusesByStoreName(); [$sql, $bindings] = $this->statusOrderBy($statuses[$sortKey], $labels, $direction); $query->orderByRaw($sql, $bindings)->orderByDesc('id'); - } elseif (($filters['_sort'] ?? '') === 'flow_status') { + } elseif (in_array($filters['_sort'] ?? '', ['flow_type', 'approval_status'], true)) { $statuses ??= $this->statusesByStoreName(); - [$sql, $bindings] = $this->statusOrderBy($statuses['flow'], self::FLOW_STATUS_LABELS, $direction); + $sortedKey = $filters['_sort'] === 'flow_type' ? 'flow_type' : 'flow'; + $labels = $filters['_sort'] === 'flow_type' ? self::FLOW_TYPE_LABELS : self::FLOW_STATUS_LABELS; + [$sql, $bindings] = $this->statusOrderBy($statuses[$sortedKey], $labels, $direction); $query->orderByRaw($sql, $bindings)->orderByDesc('id'); } else { $query->orderByDesc('id'); @@ -154,7 +159,8 @@ class StoreAccountMirrorRepository 'account_group_name' => $row->accountGroupName, 'authorization_status' => $statuses['authorization'][$row->storeName] ?? '', 'store_status' => $statuses['store'][$row->storeName] ?? '', - 'flow_status' => $statuses['flow'][$row->storeName] ?? '', + 'flow_type' => $statuses['flow_type'][$row->storeName] ?? '', + 'approval_status' => $statuses['flow'][$row->storeName] ?? '', 'created_at' => $row->createdAt, 'updated_at' => $row->updatedAt, ]; @@ -213,43 +219,46 @@ class StoreAccountMirrorRepository : ($timestamp >= $now ? '已授权' : '已过期'); } - $closeFlows = $this->closeFlowStatusByName(); - $result = ['store' => [], 'authorization' => [], 'flow' => []]; + $flows = $this->latestFlowByStoreName(); + $result = ['store' => [], 'authorization' => [], 'flow' => [], 'flow_type' => []]; foreach ($this->mirror->connection()->table($this->datasets->get(self::DATASET)->targetTable()) ->select('storeName')->whereNotNull('storeName')->distinct()->pluck('storeName') as $name) { $name = (string) $name; $key = $this->shopKey($name); $result['store'][$name] = $storeStates[$key] ?? ''; $result['authorization'][$name] = $authorizations[$key] ?? ''; - $result['flow'][$name] = $closeFlows[$key] ?? ''; + $result['flow_type'][$name] = $flows[$key]['flow_type'] ?? ''; + $result['flow'][$name] = $flows[$key]['approval_status'] ?? ''; } - Cache::put(self::STATUS_CACHE_KEY, $result, now()->addMinutes(10)); + // 状态取自店铺档案与流程镜像,变化很慢;全量算一次要几秒,缓存久一点。 + Cache::put(self::STATUS_CACHE_KEY, $result, now()->addMinutes(30)); return $result; } /** - * 账号店铺 -> 关店流程状态:按 ERP 店铺名关联 jiansun.dbo.fys_shopInfo_flow 的 - * 关店流程(flowType=10),同一店铺取最近一单,状态用 OA 流程表的审批状态文案。 + * 账号店铺 -> 最近一单店铺申请流程(开店/变更/关店/主体注销自动)的类型与审批状态。 * - * @return array 归一键 => 流程状态 + * 按 ERP 店铺名关联 jiansun.dbo.fys_shopInfo_flow,同一店铺取申请时间最近的一单; + * 审批状态用中台库 OA 流程表(dbo.flow_flow)的字典文案。 + * + * @return array 归一键 => 流程信息 */ - private function closeFlowStatusByName(): array + private function latestFlowByStoreName(): array { try { - $form = $this->datasets->get(self::CLOSE_FLOW_FORM_DATASET)->targetTable(); - $oa = $this->datasets->get(self::CLOSE_FLOW_OA_DATASET)->targetTable(); + $form = $this->datasets->get(self::FLOW_FORM_DATASET)->targetTable(); + $oa = $this->datasets->get(self::FLOW_OA_DATASET)->targetTable(); } catch (\Throwable) { - // 关店流程数据集尚未接入时,这一列留空即可,不影响其它列。 + // 店铺申请流程数据集尚未接入时,这两列留空即可,不影响其它列。 return []; } try { $rows = $this->mirror->connection()->table($form . ' as f') ->leftJoin($oa . ' as o', 'o.flowId', '=', 'f.flowId') - ->where('f.flowType', self::CLOSE_FLOW_TYPE) - ->select(['f.erpShopName as erp_shop_name', 'o.flowStatus as flow_status']) + ->select(['f.erpShopName as erp_shop_name', 'f.flowType as flow_type', 'o.flowStatus as flow_status']) ->orderByDesc('f.applyTime')->orderByDesc('f.Id') ->get(); } catch (\Throwable $exception) { @@ -258,18 +267,22 @@ class StoreAccountMirrorRepository return []; } - $statuses = []; + $flows = []; foreach ($rows as $row) { $key = $this->shopKey($row->erp_shop_name); - if ($key === '' || array_key_exists($key, $statuses)) { + if ($key === '' || array_key_exists($key, $flows)) { continue; // 已按申请时间倒序,首次出现的就是最近一单 } - $statuses[$key] = $row->flow_status === null - ? '' - : (ShopInfoFlowMirrorRepository::APPROVAL_STATUSES[(int) $row->flow_status] ?? (string) $row->flow_status); + $type = (int) $row->flow_type; + $flows[$key] = [ + 'flow_type' => ShopInfoFlowMirrorRepository::FLOW_TYPES[$type] ?? ('其它(' . $type . ')'), + 'approval_status' => $row->flow_status === null + ? '' + : (ShopInfoFlowMirrorRepository::APPROVAL_STATUSES[(int) $row->flow_status] ?? (string) $row->flow_status), + ]; } - return $statuses; + return $flows; } /** diff --git a/resources/views/business/pages/partials/directory-table.blade.php b/resources/views/business/pages/partials/directory-table.blade.php index 64575c74..31b16186 100644 --- a/resources/views/business/pages/partials/directory-table.blade.php +++ b/resources/views/business/pages/partials/directory-table.blade.php @@ -1142,8 +1142,9 @@ 'store-management/store-accounts' => [ 'authorization_status' => ['已授权', '已过期', '未授权'], 'store_status' => ['正常', '关闭中', '已关闭'], - // 店铺关店流程的审批状态(取该店铺最近一单关店流程)。 - 'flow_status' => ['流程中', '待审批', '完成', '拒绝', '撤回', '暂存', '待签署', '已签署'], + // 该店铺最近一单申请流程(开店/变更/关店/主体注销自动)的类型与审批状态。 + 'flow_type' => ['店铺开店', '店铺变更', '店铺关店', '主体注销自动流程'], + 'approval_status' => ['流程中', '待审批', '完成', '拒绝', '撤回', '暂存', '待签署', '已签署'], ], 'financial-risk-control/process-statistics' => [ 'flow_type' => ['店铺开店', '店铺变更', '店铺关店', '主体注销自动流程'], diff --git a/tests/Feature/Business/StoreAccountPasswordsTest.php b/tests/Feature/Business/StoreAccountPasswordsTest.php index e5b3ce05..48c1c384 100644 --- a/tests/Feature/Business/StoreAccountPasswordsTest.php +++ b/tests/Feature/Business/StoreAccountPasswordsTest.php @@ -317,11 +317,13 @@ class StoreAccountPasswordsTest extends FeatureTestCase // 账号分组名称后面新增的两列。 ->assertSeeText('授权状态') ->assertSeeText('店铺状态') - ->assertSeeText('流程状态') + ->assertSeeText('流程类型') + ->assertSeeText('审批状态') // 授权状态 / 店铺状态按可搜索多选渲染(提交为 xxx[])。 ->assertSee('data-directory-multi-select="authorization_status"', false) ->assertSee('data-directory-multi-select="store_status"', false) - ->assertSee('data-directory-multi-select="flow_status"', false) + ->assertSee('data-directory-multi-select="flow_type"', false) + ->assertSee('data-directory-multi-select="approval_status"', false) ->assertSee('CaiWuMultiSelect', false) ->assertDontSee('data-password-dialog', false) ->assertDontSee('data-store-password="view"', false) diff --git a/tests/Unit/EcommerceFinance/StoreAccountMirrorRepositoryTest.php b/tests/Unit/EcommerceFinance/StoreAccountMirrorRepositoryTest.php index 0e6fa2dd..f75ca83c 100644 --- a/tests/Unit/EcommerceFinance/StoreAccountMirrorRepositoryTest.php +++ b/tests/Unit/EcommerceFinance/StoreAccountMirrorRepositoryTest.php @@ -120,7 +120,7 @@ class StoreAccountMirrorRepositoryTest extends TestCase $this->assertSame(['A店铺', 'B店铺', 'C店铺'], array_column($sorted['data'], 'store_name')); } - public function test_it_shows_the_store_close_flow_status(): void + public function test_it_shows_the_latest_application_flow(): void { $db = DB::connection('business_mirror'); // 账号表用的就是带平台前缀的 ERP 店铺名(关店流程里可能写成下划线,需要归一)。 @@ -134,7 +134,7 @@ class StoreAccountMirrorRepositoryTest extends TestCase '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'], @@ -145,12 +145,20 @@ class StoreAccountMirrorRepositoryTest extends TestCase ]); $rows = collect($this->repository->paginate(1, 10)['data'])->keyBy('store_name'); - $this->assertSame('流程中', $rows['拼多多-甲店']['flow_status']); - $this->assertSame('完成', $rows['拼多多-乙店']['flow_status']); - $this->assertSame('', $rows['拼多多-丙店']['flow_status']); + $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, ['flow_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