AP6 增加审批催办与效率摘要
这个提交包含在:
@@ -46,6 +46,9 @@ type WorkflowSubmitResponse = {
|
||||
};
|
||||
|
||||
type WorkflowTaskDetailResponse = {
|
||||
task?: {
|
||||
status: string;
|
||||
};
|
||||
history: Array<{
|
||||
action: string;
|
||||
actorId: string | null;
|
||||
@@ -67,6 +70,24 @@ type NotificationDeliveryListResponse = {
|
||||
items: unknown[];
|
||||
};
|
||||
|
||||
type NotificationListResponse = {
|
||||
items: Array<{
|
||||
type: string;
|
||||
targetId: string | null;
|
||||
title: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
type WorkflowTaskSummaryResponse = {
|
||||
pendingTotal: number;
|
||||
overdueTotal: number;
|
||||
byEntityType: Array<{
|
||||
key: string;
|
||||
pendingTotal: number;
|
||||
overdueTotal: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
type LeaveTypeListResponse = {
|
||||
items: Array<{
|
||||
id: string;
|
||||
@@ -640,6 +661,73 @@ test.describe("R3 权限直连审计", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("AP6 审批催办与待办汇总:申请人可催办且重复催办被限制", async () => {
|
||||
const employee = await createAuthenticatedApiClient(EMPLOYEE);
|
||||
const manager = await createAuthenticatedApiClient(MANAGER);
|
||||
const admin = await createAuthenticatedApiClient(ADMIN);
|
||||
const clients = [employee, manager, admin];
|
||||
|
||||
try {
|
||||
const leave = await createSubmittedLeave(employee.api, `ap6-remind-${Date.now()}`);
|
||||
|
||||
const summaryBefore = await apiGetData<WorkflowTaskSummaryResponse>(manager.api, "/api/workflow/tasks/summary");
|
||||
expect(summaryBefore.pendingTotal).toBeGreaterThanOrEqual(1);
|
||||
expect(summaryBefore.byEntityType.some((item) => item.key === "leave" && item.pendingTotal > 0)).toBe(true);
|
||||
|
||||
const managerRemind = await manager.api.post(`/api/workflow/tasks/${leave.currentTaskId}/remind`, { data: {} });
|
||||
expectStatus(managerRemind.status(), [403], "manager POST workflow remind should not act as applicant");
|
||||
|
||||
const adminRemind = await admin.api.post(`/api/workflow/tasks/${leave.currentTaskId}/remind`, { data: {} });
|
||||
expectStatus(adminRemind.status(), [403], "admin-only POST workflow remind should not act as applicant");
|
||||
|
||||
const reminded = await apiPostData<{ outcome: string; notificationCount: number }>(
|
||||
employee.api,
|
||||
`/api/workflow/tasks/${leave.currentTaskId}/remind`,
|
||||
{},
|
||||
);
|
||||
expect(reminded.outcome).toBe("reminded");
|
||||
expect(reminded.notificationCount).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const duplicate = await employee.api.post(`/api/workflow/tasks/${leave.currentTaskId}/remind`, { data: {} });
|
||||
expectStatus(duplicate.status(), [409], "duplicate remind should hit 24h cooldown");
|
||||
|
||||
const workflowDetail = await apiGetData<WorkflowTaskDetailResponse>(manager.api, `/api/workflow/tasks/${leave.currentTaskId}`);
|
||||
expect(workflowDetail.task?.status).toBe("pending");
|
||||
expect(workflowDetail.history.some((item) => item.action === "remind" && item.actorId === employee.me.id)).toBe(true);
|
||||
expectNoSensitiveKeys(workflowDetail.history);
|
||||
|
||||
const notifications = await apiGetData<NotificationListResponse>(
|
||||
manager.api,
|
||||
"/api/notifications?type=workflow.task_reminded&page=1&pageSize=20",
|
||||
);
|
||||
expect(
|
||||
notifications.items.some((item) => item.targetId === leave.currentTaskId && item.type === "workflow.task_reminded"),
|
||||
).toBe(true);
|
||||
expectNoSensitiveKeys(notifications);
|
||||
|
||||
await expectAuditAction(
|
||||
admin.api,
|
||||
"WORKFLOW_TASK_REMINDED",
|
||||
(item) => item.targetId === leave.currentTaskId && item.actorId === employee.me.id,
|
||||
);
|
||||
|
||||
const systemSummary = await apiGetData<WorkflowTaskSummaryResponse>(
|
||||
admin.api,
|
||||
"/api/workflow/tasks/summary?scope=system",
|
||||
);
|
||||
expect(systemSummary.pendingTotal).toBeGreaterThanOrEqual(summaryBefore.pendingTotal);
|
||||
await expectApiStatus({
|
||||
role: "employee",
|
||||
api: employee.api,
|
||||
path: "/api/workflow/tasks/summary?scope=system",
|
||||
statuses: [403],
|
||||
note: "普通员工不得查看全局审批效率摘要",
|
||||
});
|
||||
} finally {
|
||||
await Promise.all(clients.map((client) => client.dispose()));
|
||||
}
|
||||
});
|
||||
|
||||
test("PA6 审计留痕:登录、审批历史、附件下载、导出与通知投递可追溯且脱敏", async () => {
|
||||
const employee = await createAuthenticatedApiClient(EMPLOYEE);
|
||||
const manager = await createAuthenticatedApiClient(MANAGER);
|
||||
|
||||
在新工单中引用
屏蔽一个用户