feat(G2-1,G2-2,G2-3): 扩展预算编号与工作流跨域桥接

这个提交包含在:
2026-03-07 18:59:44 +08:00
父节点 09b6f4fbb0
当前提交 52558f22c3
共修改 12 个文件,包含 1211 行新增和 175 行删除
+127 -26
查看文件
@@ -335,6 +335,77 @@ async function loadReimbursementNotifyContext(tx, reimbursementId) {
});
}
function getWorkflowEntityMeta(entityType, entityRecord, entityId) {
const normalizedEntityType = entityType === "procurement" ? "purchase_request" : entityType;
if (normalizedEntityType === "reimbursement") {
return {
label: "报销单",
reference: entityRecord?.form_number ?? entityId,
targetType: "reimbursement_form",
detailPath: `/reimbursements/${entityId}`,
};
}
if (normalizedEntityType === "leave") {
return {
label: "请假单",
reference: entityId,
targetType: "leave_request",
detailPath: "/leave-requests",
};
}
if (normalizedEntityType === "purchase_request") {
return {
label: "采购申请",
reference: entityRecord?.request_no ?? entityId,
targetType: "purchase_request",
detailPath: "/purchase-requests",
};
}
return {
label: "审批单",
reference: entityId,
targetType: normalizedEntityType,
detailPath: `/workflow/tasks/${entityId}`,
};
}
function buildPendingTaskCopy(entityMeta, { transfer = false } = {}) {
return {
title: `【待办】${entityMeta.label} ${entityMeta.reference} ${transfer ? "转审待处理" : "待审批"}`,
body: transfer
? `${entityMeta.label} ${entityMeta.reference} 已转审给你,请及时处理。`
: `${entityMeta.label} ${entityMeta.reference} 已流转到你,请及时处理。`,
};
}
function buildApprovedCopy(entityMeta) {
return {
title: `【已批准】${entityMeta.label} ${entityMeta.reference}`,
body:
entityMeta.targetType === "reimbursement_form"
? `${entityMeta.label} ${entityMeta.reference} 已审批通过,等待财务付款。`
: `${entityMeta.label} ${entityMeta.reference} 已审批通过。`,
};
}
function buildRejectedCopy(entityMeta, comment) {
return {
title: `【已驳回】${entityMeta.label} ${entityMeta.reference}`,
body: `${entityMeta.label} ${entityMeta.reference} 已被驳回:${comment}`,
};
}
function buildReturnedCopy(entityMeta, comment) {
return {
title: `【已退回】${entityMeta.label} ${entityMeta.reference}`,
body: `${entityMeta.label} ${entityMeta.reference} 已退回:${comment}`,
};
}
async function createSkipHistoryRecords(tx, workflowInstanceId, actorId, ownerId, skippedSteps) {
for (const skipped of skippedSteps) {
await tx.workflowHistory.create({
@@ -892,6 +963,12 @@ async function workflowRoutes(fastify) {
tx,
workflowInstance.entity_id,
);
const entityMeta = getWorkflowEntityMeta(
workflowInstance.entity_type,
reimbursement,
workflowInstance.entity_id,
);
const pendingCopy = buildPendingTaskCopy(entityMeta);
const recipients = await resolveRecipientsForAssignee(tx, {
assigneeId: actionable.assigneeId,
assigneeRole: actionable.assigneeRole,
@@ -900,8 +977,8 @@ async function workflowRoutes(fastify) {
await createNotifications(tx, {
recipientEmployeeIds: recipients,
type: "workflow.task_assigned",
title: `【待办】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 待审批`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已流转到你,请及时处理。`,
title: pendingCopy.title,
body: pendingCopy.body,
targetType: "workflow_task",
targetId: nextTask.id,
});
@@ -909,8 +986,8 @@ async function workflowRoutes(fastify) {
emailTasks.push({
type: "workflow.task_assigned",
recipientEmployeeIds: recipients,
subject: `【待办】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 待审批`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已流转到你,请及时处理。`,
subject: pendingCopy.title,
body: pendingCopy.body,
targetPath: `/workflow/tasks/${nextTask.id}`,
});
} catch (error) {
@@ -1013,6 +1090,12 @@ async function workflowRoutes(fastify) {
if (workflowInstance.entity_type === "reimbursement") {
try {
const reimbursement = await loadReimbursementNotifyContext(tx, workflowInstance.entity_id);
const entityMeta = getWorkflowEntityMeta(
workflowInstance.entity_type,
reimbursement,
workflowInstance.entity_id,
);
const approvedCopy = buildApprovedCopy(entityMeta);
const recipients = Array.from(
new Set([reimbursement?.owner_id, reimbursement?.submitter_id].filter(Boolean)),
);
@@ -1020,18 +1103,18 @@ async function workflowRoutes(fastify) {
await createNotifications(tx, {
recipientEmployeeIds: recipients,
type: "reimbursement.approved",
title: `【已批准】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id}`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已审批通过,等待财务付款。`,
targetType: "reimbursement_form",
title: approvedCopy.title,
body: approvedCopy.body,
targetType: entityMeta.targetType,
targetId: workflowInstance.entity_id,
});
emailTasks.push({
type: "reimbursement.approved",
recipientEmployeeIds: recipients,
subject: `【已批准】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id}`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已审批通过,等待财务付款。`,
targetPath: `/reimbursements/${workflowInstance.entity_id}`,
subject: approvedCopy.title,
body: approvedCopy.body,
targetPath: entityMeta.detailPath,
});
} catch (error) {
fastify.log.error(
@@ -1179,6 +1262,12 @@ async function workflowRoutes(fastify) {
if (workflowInstance.entity_type === "reimbursement") {
try {
const reimbursement = await loadReimbursementNotifyContext(tx, workflowInstance.entity_id);
const entityMeta = getWorkflowEntityMeta(
workflowInstance.entity_type,
reimbursement,
workflowInstance.entity_id,
);
const rejectedCopy = buildRejectedCopy(entityMeta, comment);
const recipients = Array.from(
new Set([reimbursement?.owner_id, reimbursement?.submitter_id].filter(Boolean)),
);
@@ -1186,18 +1275,18 @@ async function workflowRoutes(fastify) {
await createNotifications(tx, {
recipientEmployeeIds: recipients,
type: "reimbursement.rejected",
title: `【已驳回】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id}`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已被驳回:${comment}`,
targetType: "reimbursement_form",
title: rejectedCopy.title,
body: rejectedCopy.body,
targetType: entityMeta.targetType,
targetId: workflowInstance.entity_id,
});
emailTasks.push({
type: "reimbursement.rejected",
recipientEmployeeIds: recipients,
subject: `【已驳回】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id}`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已被驳回:${comment}`,
targetPath: `/reimbursements/${workflowInstance.entity_id}`,
subject: rejectedCopy.title,
body: rejectedCopy.body,
targetPath: entityMeta.detailPath,
});
} catch (error) {
fastify.log.error(
@@ -1344,6 +1433,12 @@ async function workflowRoutes(fastify) {
if (workflowInstance.entity_type === "reimbursement") {
try {
const reimbursement = await loadReimbursementNotifyContext(tx, workflowInstance.entity_id);
const entityMeta = getWorkflowEntityMeta(
workflowInstance.entity_type,
reimbursement,
workflowInstance.entity_id,
);
const returnedCopy = buildReturnedCopy(entityMeta, comment);
const recipients = Array.from(
new Set([reimbursement?.owner_id, reimbursement?.submitter_id].filter(Boolean)),
);
@@ -1351,18 +1446,18 @@ async function workflowRoutes(fastify) {
await createNotifications(tx, {
recipientEmployeeIds: recipients,
type: "reimbursement.returned",
title: `【已退回】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id}`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已退回:${comment}`,
targetType: "reimbursement_form",
title: returnedCopy.title,
body: returnedCopy.body,
targetType: entityMeta.targetType,
targetId: workflowInstance.entity_id,
});
emailTasks.push({
type: "reimbursement.returned",
recipientEmployeeIds: recipients,
subject: `【已退回】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id}`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已退回:${comment}`,
targetPath: `/reimbursements/${workflowInstance.entity_id}`,
subject: returnedCopy.title,
body: returnedCopy.body,
targetPath: entityMeta.detailPath,
});
} catch (error) {
fastify.log.error(
@@ -1475,11 +1570,17 @@ async function workflowRoutes(fastify) {
try {
const reimbursement = await loadReimbursementNotifyContext(tx, workflowInstance.entity_id);
const entityMeta = getWorkflowEntityMeta(
workflowInstance.entity_type,
reimbursement,
workflowInstance.entity_id,
);
const pendingCopy = buildPendingTaskCopy(entityMeta, { transfer: true });
await createNotifications(tx, {
recipientEmployeeIds: [toApproverId],
type: "workflow.task_assigned",
title: `【待办】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 转审待处理`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已转审给你,请及时处理。`,
title: pendingCopy.title,
body: pendingCopy.body,
targetType: "workflow_task",
targetId: taskId,
});
@@ -1487,8 +1588,8 @@ async function workflowRoutes(fastify) {
emailTasks.push({
type: "workflow.task_assigned",
recipientEmployeeIds: [toApproverId],
subject: `【待办】报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 转审待处理`,
body: `报销单 ${reimbursement?.form_number ?? workflowInstance.entity_id} 已转审给你,请及时处理。`,
subject: pendingCopy.title,
body: pendingCopy.body,
targetPath: `/workflow/tasks/${taskId}`,
});
} catch (error) {