From 25c438aa7a1927557fa1315bb4cbece5d4e48524 Mon Sep 17 00:00:00 2001 From: shunli Date: Wed, 13 May 2026 20:39:46 +0800 Subject: [PATCH] =?UTF-8?q?WF8-10=20=E7=A7=BB=E5=8A=A8=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=A4=84=E7=90=86=E5=8E=86=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/MobileApprovalsPage.tsx | 41 +++++++++++++++++++ .../e2e/dg7-mobile-information-dedupe.spec.ts | 30 +++++++++++++- ...-13_18-23-52-wf8-approval-notification.csv | 2 +- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/client/src/pages/MobileApprovalsPage.tsx b/client/src/pages/MobileApprovalsPage.tsx index 069a30fb..23e83240 100644 --- a/client/src/pages/MobileApprovalsPage.tsx +++ b/client/src/pages/MobileApprovalsPage.tsx @@ -143,6 +143,27 @@ function workflowStatusLabel(status: string) { return status; } +function workflowActionLabel(action: string | null | undefined) { + if (!action) { + return "-"; + } + + const labels: Record = { + submit: "提交", + approve: "通过", + reject: "驳回", + return: "退回", + transfer: "转审", + withdraw: "撤回", + remind: "催办", + overdue_remind: "超时提醒", + cancel: "取消", + skip: "跳过", + }; + + return labels[action] ?? action; +} + function businessStatusLabel(status: string | null | undefined) { const labels: Record = { draft: "草稿", @@ -1322,6 +1343,26 @@ export function MobileApprovalsPage() { ) : null} +
+
+

处理历史

+ {selectedTask.history.length} 条 +
+ {selectedTask.history.length ? ( + selectedTask.history.map((item) => ( +
+
+ {workflowActionLabel(item.action)} · {item.actorName || item.actorEmployeeNo || item.actorId} +
+
{item.comment || "-"}
+
{formatDateTime(item.createdAt)}
+
+ )) + ) : ( +

暂无历史记录

+ )} +
+ {!selectedTaskAllowsActions ? (

当前任务已处理,仅供查看,不支持再次审批。 diff --git a/client/tests/e2e/dg7-mobile-information-dedupe.spec.ts b/client/tests/e2e/dg7-mobile-information-dedupe.spec.ts index b89147ff..ff4f3be3 100644 --- a/client/tests/e2e/dg7-mobile-information-dedupe.spec.ts +++ b/client/tests/e2e/dg7-mobile-information-dedupe.spec.ts @@ -272,6 +272,31 @@ function workflowTaskSummary(status: "pending" | "completed" = "pending") { function workflowTaskDetail(status: "pending" | "completed" = "pending") { const isCompleted = status === "completed"; + const history = [ + { + id: "history-workflow-submit-dg7", + taskId: null, + actorId: "employee-dg7-employee", + actorName: "测试员工", + actorEmployeeNo: "E-DG7", + action: "submit", + comment: "提交审批", + payload: null, + createdAt: "2026-05-12T08:00:00.000Z", + }, + { + id: isCompleted ? "history-workflow-approve-dg7" : "history-workflow-transfer-dg7", + taskId: isCompleted ? "task-dg7-completed" : "task-dg7", + actorId: "employee-dg7-manager", + actorName: "测试审批人", + actorEmployeeNo: "M-DG7", + action: isCompleted ? "approve" : "transfer", + comment: isCompleted ? "同意报销" : "转给财务复核", + payload: isCompleted ? null : { transferToEmployeeId: "employee-dg7-finance" }, + createdAt: isCompleted ? "2026-05-12T09:00:00.000Z" : "2026-05-12T08:30:00.000Z", + }, + ]; + return { task: { id: isCompleted ? "task-dg7-completed" : "task-dg7", @@ -309,7 +334,7 @@ function workflowTaskDetail(status: "pending" | "completed" = "pending") { purchaseRequest: null, travelRequest: null, allowedActions: isCompleted ? [] : ["approve", "reject", "return", "transfer"], - history: [], + history, }; } @@ -874,6 +899,9 @@ test.describe("MIF7 移动信息架构整改", () => { await expect(mobile.page.getByTestId("mobile-bottom-nav")).toHaveCount(0); await expect(mobile.page.getByTestId("mobile-approvals-detail")).not.toContainText("业务单号"); await expect(mobile.page.getByTestId("mobile-approvals-detail")).toContainText("报销单 BX20260512001"); + await expect(mobile.page.getByTestId("mobile-approvals-history")).toContainText("提交 · 测试员工"); + await expect(mobile.page.getByTestId("mobile-approvals-history")).toContainText("转审 · 测试审批人"); + await expect(mobile.page.getByTestId("mobile-approvals-history")).not.toContainText("transferToEmployeeId"); await expectNoMobileShellRegression(mobile.page); await mobile.page.screenshot({ path: screenshotPath("manager-approval-push-detail"), fullPage: true }); diff --git a/issues/2026-05-13_18-23-52-wf8-approval-notification.csv b/issues/2026-05-13_18-23-52-wf8-approval-notification.csv index 37c97759..1c2567d9 100644 --- a/issues/2026-05-13_18-23-52-wf8-approval-notification.csv +++ b/issues/2026-05-13_18-23-52-wf8-approval-notification.csv @@ -9,7 +9,7 @@ WF8-6,通知失败可追溯,"补强企业微信、站内和可选邮件通知失 WF8-7,邮件通道决策记录,"决定邮件通道是否进入当前验收,若不进入则冻结并说明替代通知通道。","1) 邮件是否验收有明确结论;2) 不影响企业微信和站内通知验收;3) 冻结原因可追溯;4) 后续解锁条件清楚。","流程级验证:人工核对需求规约和 Issue Notes 中邮件通道决策。",manual,DONE,DONE,DONE,docs/需求-统一审批中心规约.md|issues/2026-05-13_18-23-52-wf8-approval-notification.csv,"[""WF8-1""]",邮件通道当前冻结,不作为 WF8 硬门禁;站内通知和企业微信个人应用消息继续验收;解锁条件已写入规约和本 Notes。 WF8-8,三渠道通知验收设计,"设计站内、企业微信个人应用消息和邮件或冻结替代通道的三渠道验收口径。","1) 三渠道或二渠道加冻结说明清楚;2) deeplink 回 OA;3) 缺配置时 SKIP 不等于 PASS;4) 真机和公网边界明确。","流程级验证:人工审查通知矩阵;必要时执行 live readiness 和企业微信真机验证。",manual|bash|playwright,DONE,DONE,DONE,docs/试运行与自动化验证手册.md|docs/需求-企业微信基础设施规约.md,"[""WF8-6"",""WF8-7""]",Runbook 已新增 WF8 通知渠道验收矩阵:站内/企业微信个人应用/邮件冻结/失败追溯四类口径;明确本地自动化/公网匿名/公网带凭据/企业微信真机证据边界;rg Test_Method 已通过;本任务仅设计验收口径 未执行真机验收。 WF8-9,催办和超时提醒回归,"回归既有催办和超时提醒,避免转审或通知增强引入倒退。","1) 催办不改变审批状态;2) 超时提醒不重复刷屏;3) 通知对象正确;4) 历史记录可追溯。","流程级验证:创建待审批任务 -> 触发催办或超时提醒 -> 检查通知和 workflow history。",node|bash|manual,DONE,DONE,DONE,server/src|server/scripts|docs/需求-统一审批中心规约.md,"[""WF8-6""]",AP6 后提醒能力是当前事实;已跑 E2E AP6 审批催办与待办汇总 1 passed 覆盖申请人催办/经理和 admin 禁止/重复 24h 限制/history/notification/audit/summary;已跑 workflow.travel、workflowOverdueReminderJob、notificationTypes 单测 7 passed 覆盖超时提醒 job 和通知类型;node --check workflow.js/workflowOverdueReminderJob.js 通过。 -WF8-10,审批历史一致性,"确保转审、催办、通过、驳回和通知事件在审批历史中一致展示。","1) 历史顺序清楚;2) 操作人和目标人可解释;3) 不泄露敏感字段;4) PC 和移动展示一致。","流程级验证:提交审批 -> 转审 -> 催办 -> 审批处理 -> PC 和移动查看历史。",playwright|node|manual,TODO,TODO,TODO,server/src|client/src,"[""WF8-3"",""WF8-4"",""WF8-5"",""WF8-9""]",待批准方案:PC WorkflowTaskDetailPage 已展示 detail.history;MobileApprovalsPage 已获取同一 fetchWorkflowTaskDetail 结果但 push detail 未渲染 selectedTask.history;批准后仅改 client/src/pages/MobileApprovalsPage.tsx 新增移动处理历史卡片和 workflowActionLabel 映射 展示动作 操作人 意见 时间 空态;不改后端 不改数据结构 不引入新依赖;验证 client typecheck/build 和 PC/移动审批历史一致性 Playwright。 +WF8-10,审批历史一致性,"确保转审、催办、通过、驳回和通知事件在审批历史中一致展示。","1) 历史顺序清楚;2) 操作人和目标人可解释;3) 不泄露敏感字段;4) PC 和移动展示一致。","流程级验证:提交审批 -> 转审 -> 催办 -> 审批处理 -> PC 和移动查看历史。",playwright|node|manual,DONE,DONE,DONE,server/src|client/src,"[""WF8-3"",""WF8-4"",""WF8-5"",""WF8-9""]",完成:MobileApprovalsPage 渲染 selectedTask.history 的移动处理历史卡片并复用动作中文映射;仅展示动作 操作人 意见 时间和空态 不渲染 payload;不改后端 数据结构 权限或状态机;验证 npm run typecheck 通过;npm run build 通过;npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts --workers=1 --reporter=list 在启动本地 3000 服务后 5 passed。 WF8-11,通知 deeplink 回 OA 验证,"验证企业微信个人应用消息或站内通知 deeplink 能回到 OA 目标页面。","1) deeplink 指向具体业务目标;2) 不只返回通知中心;3) 未登录或未绑定有可解释提示;4) 移动布局正常。","流程级验证:触发通知 -> 点击 deeplink -> 企业微信或移动浏览器打开 OA 目标详情 -> 检查权限和页面。",playwright|manual,TODO,TODO,TODO,client/src|server/src|docs/trialrun/真机公网测试清单.md,"[""WF8-8"",""WF8-10""]",真机验收不能被自动化替代。 WF8-12,workflow reviewer 审查,"对 WF8 涉及的 workflow 状态、权限和通知副作用做独立 review。","1) 非法状态跳转无阻断;2) 权限泄漏无阻断;3) 重复审批和并发风险可控;4) 漏测项有归属。","流程级验证:reviewer 人工审查 diff、Issue CSV 和测试证据。",manual,TODO,TODO,TODO,issues/2026-05-13_18-23-52-wf8-approval-notification.csv|server/src|client/src,"[""WF8-10"",""WF8-11""]",实现后立即 review。 WF8-13,WF8 收口准备,"整理 WF8 验证摘要、通知矩阵、未完成项和后续业务影响。","1) DONE 行均有证据;2) 通知渠道结论清楚;3) 未完成项不伪装完成;4) 文档入口同步。","流程级验证:人工核对 WF8 CSV 状态、验证摘要、通知矩阵和 git diff。",manual|git,TODO,TODO,TODO,issues/2026-05-13_18-23-52-wf8-approval-notification.csv|docs/文档索引.md,"[""WF8-12""]",对应原规划 WF8-13。