fix: 店铺流程当前审批人显示全量候选 #970

Merged
renshen merged 2 commits from fix/renshen-process-approver-suggestions into main 2026-11-06 22:54:54 +08:00
4 changed files with 98 additions and 5 deletions

View File

@ -68,11 +68,19 @@ class UpstreamBusinessInformation
private function shopInfoFlowPage(int $page, int $perPage): array
{
try {
return app(\Modules\EcommerceFinance\Repositories\ShopInfoFlowMirrorRepository::class)->paginate(
$repository = app(\Modules\EcommerceFinance\Repositories\ShopInfoFlowMirrorRepository::class);
$filters = $this->pageFilters('financial-risk-control', 'process-statistics');
$payload = $repository->paginate(
$page,
$perPage,
$this->pageFilters('financial-risk-control', 'process-statistics'),
$filters,
);
request()->attributes->set(
'process-statistics.current-approvers',
$repository->currentApproverOptions($filters),
);
return $payload;
} catch (\RuntimeException $exception) {
report($exception);

View File

@ -116,6 +116,37 @@ class ShopInfoFlowMirrorRepository
return ['data' => $rows, 'total' => $total];
}
/** @param array<string,mixed> $filters
* @return list<string>
*/
public function currentApproverOptions(array $filters = []): array
{
if (! $this->ready()) {
return [];
}
$connection = $this->mirror->connection();
$form = $this->datasets->get(self::FORM_DATASET)->targetTable();
$flow = $this->datasets->get(self::FLOW_DATASET)->targetTable();
unset($filters['current_approver']);
$query = $connection->table($form . ' as f')
->leftJoin($flow . ' as o', 'o.flowId', '=', 'f.flowId')
->whereNotNull('o.approver')
->where('o.approver', '!=', '');
$this->applyFilters($query, $filters);
return $query
->pluck('o.approver')
->flatMap(static fn (mixed $value): array => preg_split('/[,,]/u', (string) $value, -1, PREG_SPLIT_NO_EMPTY) ?: [])
->map(static fn (mixed $value): string => trim((string) $value))
->filter(static fn (string $value): bool => $value !== '')
->unique()
->sort(static fn (string $left, string $right): int => strnatcasecmp($left, $right))
->values()
->all();
}
/** @param array<string,mixed> $filters */
private function applyFilters(Builder $query, array $filters): void
{

View File

@ -1921,6 +1921,12 @@
default => [],
};
$directoryMultiSelectFields = array_keys($directoryMultiSelectOptions);
$directoryQuerySuggestionOptions = match (($section['slug'] ?? '') . '/' . ($page['slug'] ?? '')) {
'financial-risk-control/process-statistics' => [
'current_approver' => (array) request()->attributes->get('process-statistics.current-approvers', []),
],
default => [],
};
$directoryColumnNames = collect($directoryFields)->pluck('name')->all();
$requestedDirectoryColumns = array_values(array_intersect(
array_filter(explode(',', (string) request('columns', ''))),
@ -2033,7 +2039,7 @@
@endforeach
</select>
@else
<input type="search" form="{{ $directoryQueryFormId }}" name="{{ $field['name'] }}" value="{{ request($field['name']) }}" autocomplete="off" placeholder="{{ $field['placeholder'] ?? ('搜索' . $field['label']) }}" data-directory-query-input="{{ $field['name'] }}" aria-label="搜索{{ $field['label'] }}">
<input type="search" form="{{ $directoryQueryFormId }}" name="{{ $field['name'] }}" value="{{ request($field['name']) }}" autocomplete="off" placeholder="{{ $field['placeholder'] ?? ('搜索' . $field['label']) }}" data-directory-query-input="{{ $field['name'] }}" data-directory-server-options="{{ json_encode($directoryQuerySuggestionOptions[$field['name']] ?? [], JSON_UNESCAPED_UNICODE) }}" aria-label="搜索{{ $field['label'] }}">
<ul class="directory-query-suggestions" data-directory-query-suggestions="{{ $field['name'] }}" hidden></ul>
@endif
</label>
@ -2088,7 +2094,7 @@
@endforeach
</select>
@else
<input type="search" form="{{ $directoryQueryFormId }}" name="{{ $field['name'] }}" value="{{ request($field['name']) }}" autocomplete="off" placeholder="{{ $field['placeholder'] ?? ('搜索' . $field['label']) }}" data-directory-query-input="{{ $field['name'] }}" aria-label="搜索{{ $field['label'] }}">
<input type="search" form="{{ $directoryQueryFormId }}" name="{{ $field['name'] }}" value="{{ request($field['name']) }}" autocomplete="off" placeholder="{{ $field['placeholder'] ?? ('搜索' . $field['label']) }}" data-directory-query-input="{{ $field['name'] }}" data-directory-server-options="{{ json_encode($directoryQuerySuggestionOptions[$field['name']] ?? [], JSON_UNESCAPED_UNICODE) }}" aria-label="搜索{{ $field['label'] }}">
<ul class="directory-query-suggestions" data-directory-query-suggestions="{{ $field['name'] }}" hidden></ul>
@endif
</label>
@ -3717,11 +3723,19 @@
if (!list) return;
const key = field.dataset.directoryQueryField;
const needle = value.trim().toLocaleLowerCase();
let serverOptions = [];
try {
serverOptions = JSON.parse(field.querySelector('[data-directory-server-options]')?.dataset.directoryServerOptions || '[]') || [];
} catch (_) {
serverOptions = [];
}
const candidates = key === 'related_people'
? rows().flatMap((row) => ['legal_representative', 'shareholders', 'supervisor', 'billing_officer']
.map((column) => (row.querySelector(`[data-column-key="${column}"]`)?.textContent || '').trim()))
: rows().map((row) => fieldValues(row, key));
const values = [...new Set(candidates.filter((item) => item && item !== '—' && (!needle || item.toLocaleLowerCase().includes(needle))))].slice(0, 8);
const values = [...new Set([...serverOptions, ...candidates].filter(
(item) => item && item !== '—' && (!needle || String(item).toLocaleLowerCase().includes(needle)),
))].slice(0, 8);
list.innerHTML = values.map((item) => `<li><button type="button" data-directory-suggestion-value="${item.replaceAll('&','&amp;').replaceAll('\"','&quot;').replaceAll('<','&lt;').replaceAll('>','&gt;')}">${item.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;')}</button></li>`).join('');
list.hidden = values.length === 0;
list.querySelectorAll('button').forEach((button) => button.addEventListener('click', () => {

View File

@ -139,6 +139,46 @@ class ShopInfoFlowMirrorRepositoryTest extends TestCase
$this->assertSame(0, $this->repository->paginate(1, 10, ['flow_id' => '不存在'])['total']);
}
public function test_current_approver_options_include_all_rows_matching_other_filters(): void
{
$db = DB::connection('business_mirror');
foreach ([
[10, 'approver-1', '查查'],
[11, 'approver-2', '玉兔'],
[12, 'approver-3', '查查,常仪'],
] as [$id, $flowId, $approver]) {
$this->insertApplication(
$id,
$flowId,
10,
'京东',
'审批人测试' . $id,
'ERP-审批人测试' . $id,
'申请人' . $id,
'2026-10-05 09:00:00',
);
$db->table('oa_flow_flow')->insert([
'Id' => $id,
'flowId' => $flowId,
'flowType' => 10,
'flowStatus' => 1,
'subject' => '[店铺关店-审批人测试' . $id . ']',
'approver' => $approver,
'updateTime' => '2026-10-05 10:00:00',
]);
}
$options = $this->repository->currentApproverOptions([
'flow_type' => ['店铺关店'],
'current_approver' => '玉兔',
]);
$this->assertEqualsCanonicalizing(['查查', '玉兔', '常仪'], $options);
$this->assertSame(['审核人A'], $this->repository->currentApproverOptions([
'flow_type' => ['店铺变更'],
]));
}
public function test_it_groups_waiting_time_by_days_and_filters_selected_day_buckets(): void
{
Carbon::setTestNow('2026-11-05 12:00:00');