WF8-10 移动审批展示处理历史

这个提交包含在:
2026-05-13 20:39:46 +08:00
父节点 0692a48e63
当前提交 25c438aa7a
共修改 3 个文件,包含 71 行新增和 2 行删除
+41
查看文件
@@ -143,6 +143,27 @@ function workflowStatusLabel(status: string) {
return status; return status;
} }
function workflowActionLabel(action: string | null | undefined) {
if (!action) {
return "-";
}
const labels: Record<string, string> = {
submit: "提交",
approve: "通过",
reject: "驳回",
return: "退回",
transfer: "转审",
withdraw: "撤回",
remind: "催办",
overdue_remind: "超时提醒",
cancel: "取消",
skip: "跳过",
};
return labels[action] ?? action;
}
function businessStatusLabel(status: string | null | undefined) { function businessStatusLabel(status: string | null | undefined) {
const labels: Record<string, string> = { const labels: Record<string, string> = {
draft: "草稿", draft: "草稿",
@@ -1322,6 +1343,26 @@ export function MobileApprovalsPage() {
) : null} ) : null}
</div> </div>
<div className="space-y-2" data-testid="mobile-approvals-history">
<div className="flex items-center justify-between gap-3">
<h4 className="text-sm font-semibold text-zinc-900">处理历史</h4>
<span className="text-xs text-zinc-500">{selectedTask.history.length} 条</span>
</div>
{selectedTask.history.length ? (
selectedTask.history.map((item) => (
<article className="mobile-list-card text-sm" data-testid="mobile-approvals-history-item" key={item.id}>
<div className="font-medium text-zinc-900">
{workflowActionLabel(item.action)} · {item.actorName || item.actorEmployeeNo || item.actorId}
</div>
<div className="mt-1 text-zinc-600">{item.comment || "-"}</div>
<div className="mt-1 text-xs text-zinc-500">{formatDateTime(item.createdAt)}</div>
</article>
))
) : (
<p className="text-sm text-zinc-500">暂无历史记录</p>
)}
</div>
{!selectedTaskAllowsActions ? ( {!selectedTaskAllowsActions ? (
<p className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm text-zinc-600"> <p className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm text-zinc-600">
当前任务已处理,仅供查看,不支持再次审批。 当前任务已处理,仅供查看,不支持再次审批。
@@ -272,6 +272,31 @@ function workflowTaskSummary(status: "pending" | "completed" = "pending") {
function workflowTaskDetail(status: "pending" | "completed" = "pending") { function workflowTaskDetail(status: "pending" | "completed" = "pending") {
const isCompleted = status === "completed"; 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 { return {
task: { task: {
id: isCompleted ? "task-dg7-completed" : "task-dg7", id: isCompleted ? "task-dg7-completed" : "task-dg7",
@@ -309,7 +334,7 @@ function workflowTaskDetail(status: "pending" | "completed" = "pending") {
purchaseRequest: null, purchaseRequest: null,
travelRequest: null, travelRequest: null,
allowedActions: isCompleted ? [] : ["approve", "reject", "return", "transfer"], 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-bottom-nav")).toHaveCount(0);
await expect(mobile.page.getByTestId("mobile-approvals-detail")).not.toContainText("业务单号"); 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-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 expectNoMobileShellRegression(mobile.page);
await mobile.page.screenshot({ path: screenshotPath("manager-approval-push-detail"), fullPage: true }); await mobile.page.screenshot({ path: screenshotPath("manager-approval-push-detail"), fullPage: true });
@@ -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-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-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-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-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-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。 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。
1 ID Title Description Acceptance Test_Method Tools Dev_Status Review1_Status Regression_Status Files Dependencies Notes
9 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。
10 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 已通过;本任务仅设计验收口径 未执行真机验收。
11 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 通过。
12 WF8-10 审批历史一致性 确保转审、催办、通过、驳回和通知事件在审批历史中一致展示。 1) 历史顺序清楚;2) 操作人和目标人可解释;3) 不泄露敏感字段;4) PC 和移动展示一致。 流程级验证:提交审批 -> 转审 -> 催办 -> 审批处理 -> PC 和移动查看历史。 playwright|node|manual TODO DONE TODO DONE TODO DONE 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。 完成: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。
13 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"] 真机验收不能被自动化替代。
14 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。
15 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。