diff --git a/modules/EcommerceFinance/Repositories/DingPayableWorkflowMirrorRepository.php b/modules/EcommerceFinance/Repositories/DingPayableWorkflowMirrorRepository.php index c999bffb..48a72cb0 100644 --- a/modules/EcommerceFinance/Repositories/DingPayableWorkflowMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/DingPayableWorkflowMirrorRepository.php @@ -59,7 +59,8 @@ class DingPayableWorkflowMirrorRepository continue; } if (($filters['process_instance_id'] ?? '') !== '' - && ! str_contains((string) ($workflow['processInstanceId'] ?? ''), (string) $filters['process_instance_id'])) { + && ! str_contains((string) ($workflow['processInstanceId'] ?? ''), (string) $filters['process_instance_id']) + && ! str_contains((string) ($workflow['businessId'] ?? ''), (string) $filters['process_instance_id'])) { continue; } diff --git a/modules/EcommerceFinance/Services/DingPayableWorkflowMirrorSynchronizer.php b/modules/EcommerceFinance/Services/DingPayableWorkflowMirrorSynchronizer.php index da55a1d2..dcef66f7 100644 --- a/modules/EcommerceFinance/Services/DingPayableWorkflowMirrorSynchronizer.php +++ b/modules/EcommerceFinance/Services/DingPayableWorkflowMirrorSynchronizer.php @@ -57,7 +57,9 @@ class DingPayableWorkflowMirrorSynchronizer $generation = $resume ? (string) $state['generation'] : (string) Str::uuid(); $cursor = $resume ? (string) ($state['cursor']['processInstanceId'] ?? '') - : (string) ($this->decodeJson($state['high_watermark'] ?? null)['processInstanceId'] ?? ''); + : ($full + ? '' + : (string) ($this->decodeJson($state['high_watermark'] ?? null)['processInstanceId'] ?? '')); $rows = $resume ? (int) ($state['last_upserted_rows'] ?? 0) : 0; $batchSize = min(max($batchSize ?? (int) ($config['batch_size'] ?? 100), 1), 500); $maxBatches = max($maxBatches ?? (int) ($config['max_batches_per_run'] ?? 2), 0); @@ -78,8 +80,13 @@ class DingPayableWorkflowMirrorSynchronizer static fn (array $row): string => (string) ($row['processInstanceId'] ?? ''), $workflows, ))); + $originatorIds = array_values(array_unique(array_filter(array_map( + static fn (array $row): string => trim((string) ($row['originatorUserId'] ?? '')), + $workflows, + )))); $schemes = $this->readRelated($source, 'ding_WorkflowScheme', ['id', 'processInstanceId', 'componentType', 'name', 'value', 'extValue'], $ids, 'id'); $tasks = $this->readRelated($source, 'ding_WorkflowTask', ['processInstanceId', 'result', 'activityId', 'finishTime', 'createTime', 'userId', 'taskId', 'status', 'id'], $ids, 'id'); + $originatorNames = $this->readOriginatorNames($source, $originatorIds); $schemeById = $this->groupBy($schemes, 'processInstanceId'); $taskById = $this->groupBy($tasks, 'processInstanceId'); @@ -91,6 +98,13 @@ class DingPayableWorkflowMirrorSynchronizer if ($id === '') { continue; } + $originatorId = trim((string) ($workflow['originatorUserId'] ?? '')); + $originatorName = trim((string) ($originatorNames[$originatorId] ?? '')); + if ($originatorName !== '') { + // 保留钉钉原始用户 ID,同时把稳定的中文姓名写入镜像, + // 页面无需在请求期间再次直连上游或猜测标题内容。 + $workflow['originatorUserName'] = $originatorName; + } $payloads[] = [ 'process_instance_id' => $id, 'workflow_payload' => json_encode($workflow, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), @@ -262,6 +276,57 @@ class DingPayableWorkflowMirrorSynchronizer ); } + /** @param list $userIds @return array */ + private function readOriginatorNames(string $source, array $userIds): array + { + if ($userIds === []) { + return []; + } + + $names = []; + try { + $names = $this->readUserNameRows($source, 'hr_employee', ['userid', 'name'], $userIds); + } catch (Throwable) { + // 姓名目录短暂不可用时不应阻断应付流程同步;页面会回退显示原始用户 ID。 + } + + $missing = array_values(array_diff($userIds, array_keys($names))); + if ($missing !== []) { + try { + // 已离职申请人不一定还存在于 hr_employee,使用同一只读来源的 + // 离职视图补齐姓名;两个查询均为固定字段和参数化 IN 条件。 + $retired = $this->readUserNameRows($source, 'hr_employee_dimission', ['userid', 'realName', 'name'], $missing); + $names += $retired; + } catch (Throwable) { + // 离职目录不可用时保留已解析的在职姓名。 + } + } + + return $names; + } + + /** @param list $columns @param list $userIds @return array */ + private function readUserNameRows(string $source, string $table, array $columns, array $userIds): array + { + $placeholders = implode(',', array_fill(0, count($userIds), '?')); + $rows = $this->queries->forSource($source)->select( + 'SELECT ' . implode(', ', array_map(static fn (string $column): string => '[' . $column . ']', $columns)) + . ' FROM [rpt].[' . $table . '] WHERE [userid] IN (' . $placeholders . ')', + $userIds, + ); + $names = []; + + foreach ($rows as $row) { + $id = trim((string) ($row['userid'] ?? $row['userId'] ?? '')); + $name = trim((string) ($row['name'] ?? $row['realName'] ?? '')); + if ($id !== '' && $name !== '') { + $names[$id] = $name; + } + } + + return $names; + } + /** @param list> $rows @return array>> */ private function groupBy(array $rows, string $column): array { diff --git a/resources/views/business/pages/courier-payables/payable-cloud-warehouse-vehicle-fee.blade.php b/resources/views/business/pages/courier-payables/payable-cloud-warehouse-vehicle-fee.blade.php index f1eeefcf..b1317db5 100644 --- a/resources/views/business/pages/courier-payables/payable-cloud-warehouse-vehicle-fee.blade.php +++ b/resources/views/business/pages/courier-payables/payable-cloud-warehouse-vehicle-fee.blade.php @@ -11,6 +11,27 @@ $v = $detail[$key] ?? null; return $v === null || $v === '' ? '-' : (string) $v; }; + $statusLabel = static function (mixed $raw): string { + $status = trim((string) ($raw ?? '')); + if ($status === '') { + return '-'; + } + + return [ + 'NEW' => '待提交', + 'DRAFT' => '草稿', + 'RUNNING' => '进行中', + 'PENDING' => '待处理', + 'COMPLETED' => '已完成', + 'TERMINATED' => '已终止', + 'CANCELED' => '已取消', + 'CANCELLED' => '已取消', + 'REJECTED' => '已驳回', + 'FAILED' => '失败', + 'EXPIRED' => '已过期', + 'DELETED' => '已删除', + ][strtoupper($status)] ?? $status; + }; @endphp
@@ -42,8 +63,8 @@
- - + +
@@ -66,7 +87,7 @@
- + @@ -75,11 +96,13 @@ @php($workflow = $row['workflow'] ?? []) @php($detail = $row['detail'] ?? []) @php($fields = $row['fields'] ?? []) + @php($approvalNumber = trim((string) ($workflow['businessId'] ?? '')) ?: (string) ($row['process_instance_id'] ?? '-')) + @php($originator = trim((string) ($workflow['originatorUserName'] ?? '')) ?: trim((string) ($workflow['originatorUserId'] ?? '-'))) - + - - + + diff --git a/tests/Feature/Business/DingPayableWorkflowPageTest.php b/tests/Feature/Business/DingPayableWorkflowPageTest.php index c6532951..f9091d0f 100644 --- a/tests/Feature/Business/DingPayableWorkflowPageTest.php +++ b/tests/Feature/Business/DingPayableWorkflowPageTest.php @@ -17,9 +17,11 @@ class DingPayableWorkflowPageTest extends FeatureTestCase 'data' => [[ 'process_instance_id' => 'PROC-TEST-001', 'workflow' => [ + 'businessId' => '202607031324000376786', 'title' => '应付申请流程', 'status' => 'COMPLETED', 'originatorUserId' => 'user-1', + 'originatorUserName' => '扬灵', 'originatorDeptName' => '财务部', 'createAt' => '2026-08-28 10:00:00', 'finishTime' => '2026-08-28 11:00:00', @@ -39,6 +41,10 @@ class DingPayableWorkflowPageTest extends FeatureTestCase 'page' => 'payable-cloud-warehouse-vehicle-fee', ]))->assertOk() ->assertSee('应付云仓车费') + ->assertSee('钉钉审批编号') + ->assertSee('202607031324000376786') + ->assertSee('扬灵') + ->assertSee('已完成') ->assertSee('拉到江西仓的车费') ->assertSee('用途同时包含“仓”“车费”') ->assertSee('全部表单字段')
流程实例 ID标题状态申请人申请部门创建时间完成时间钉钉审批编号标题状态申请人申请部门创建时间完成时间 类型用途付款金额货款月份快递支付方式收款人全称开户银行银行账号 全部表单字段付款明细附件审批任务
{{ $row['process_instance_id'] ?? '-' }}{{ $approvalNumber }} {{ $workflow['title'] ?? '-' }}{{ $workflow['status'] ?? '-' }}{{ $workflow['originatorUserId'] ?? '-' }}{{ $statusLabel($workflow['status'] ?? null) }}{{ $originator }} {{ $workflow['originatorDeptName'] ?? '-' }} {{ $workflow['createAt'] ?? '-' }} {{ $workflow['finishTime'] ?? '-' }}