feat: show ding payable approval details
This commit is contained in:
parent
f8993f814a
commit
a054241331
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<string> $userIds @return array<string,string> */
|
||||
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<string> $columns @param list<string> $userIds @return array<string,string> */
|
||||
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<array<string,mixed>> $rows @return array<string,list<array<string,mixed>>> */
|
||||
private function groupBy(array $rows, string $column): array
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
<div class="ding-payable-page">
|
||||
@ -42,8 +63,8 @@
|
||||
|
||||
<form method="GET" class="ding-payable-toolbar" aria-label="应付云仓车费筛选">
|
||||
<div class="ding-payable-field">
|
||||
<label for="ding-payable-process-id">流程实例 ID(可选)</label>
|
||||
<input id="ding-payable-process-id" name="process_instance_id" value="{{ $filters['process_instance_id'] ?? '' }}" placeholder="输入流程实例 ID">
|
||||
<label for="ding-payable-process-id">审批编号或流程实例 ID(可选)</label>
|
||||
<input id="ding-payable-process-id" name="process_instance_id" value="{{ $filters['process_instance_id'] ?? '' }}" placeholder="输入钉钉审批编号或流程实例 ID">
|
||||
</div>
|
||||
<div class="ding-payable-actions">
|
||||
<button class="ding-payable-button primary" type="submit"><span class="material-icons-outlined" aria-hidden="true">search</span>查询</button>
|
||||
@ -66,7 +87,7 @@
|
||||
<div class="ding-payable-table-wrap">
|
||||
<table class="ding-payable-table">
|
||||
<thead><tr>
|
||||
<th>流程实例 ID</th><th>标题</th><th>状态</th><th>申请人</th><th>申请部门</th><th>创建时间</th><th>完成时间</th>
|
||||
<th>钉钉审批编号</th><th>标题</th><th>状态</th><th>申请人</th><th>申请部门</th><th>创建时间</th><th>完成时间</th>
|
||||
<th>类型</th><th>用途</th><th class="num">付款金额</th><th>货款月份</th><th>快递</th><th>支付方式</th><th>收款人全称</th><th>开户银行</th><th>银行账号</th>
|
||||
<th>全部表单字段</th><th>付款明细</th><th>附件</th><th>审批任务</th>
|
||||
</tr></thead>
|
||||
@ -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'] ?? '-')))
|
||||
<tr>
|
||||
<td>{{ $row['process_instance_id'] ?? '-' }}</td>
|
||||
<td>{{ $approvalNumber }}</td>
|
||||
<td>{{ $workflow['title'] ?? '-' }}</td>
|
||||
<td>{{ $workflow['status'] ?? '-' }}</td>
|
||||
<td>{{ $workflow['originatorUserId'] ?? '-' }}</td>
|
||||
<td>{{ $statusLabel($workflow['status'] ?? null) }}</td>
|
||||
<td>{{ $originator }}</td>
|
||||
<td>{{ $workflow['originatorDeptName'] ?? '-' }}</td>
|
||||
<td>{{ $workflow['createAt'] ?? '-' }}</td>
|
||||
<td>{{ $workflow['finishTime'] ?? '-' }}</td>
|
||||
|
||||
@ -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('全部表单字段')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user