feat(P19IM1-3,P19IM1-4): 接入业务事件与通知渠道后台

这个提交包含在:
2026-03-08 13:14:22 +08:00
父节点 f6d9f23553
当前提交 faff86c947
共修改 9 个文件,包含 1268 行新增和 135 行删除
+60
查看文件
@@ -3,6 +3,10 @@ const { z } = require("zod");
const { AppError } = require("../utils/AppError");
const { buildAuditLogData } = require("../utils/audit");
const { issueDocumentNumber } = require("../services/documentSequence");
const { createNotifications, resolveRecipientsForAssignee } = require("../services/notifications");
const { enqueueImNotifications } = require("../services/notifications/imEnqueue");
const { sendNotificationEmailSafe } = require("../services/email");
const { NOTIFICATION_TYPES } = require("../constants/notificationTypes");
const { sendData } = require("../utils/reply");
const UUID_SCHEMA = z.string().uuid();
@@ -79,6 +83,10 @@ function normalizeOptionalText(value) {
return trimmed ? trimmed : null;
}
function uniq(values) {
return Array.from(new Set((values ?? []).filter(Boolean)));
}
async function ensureScopeReference(tx, scopeType, scopeRefId) {
if (scopeType === "all") {
return null;
@@ -161,6 +169,7 @@ async function ensureInventoryCheck(tx, inventoryCheckId) {
completed_at: true,
created_at: true,
updated_at: true,
created_by: true,
items: {
orderBy: [{ asset: { asset_no: "asc" } }],
select: {
@@ -302,6 +311,7 @@ async function inventoryChecksRoutes(fastify) {
completed_at: true,
created_at: true,
updated_at: true,
created_by: true,
items: {
select: {
id: true,
@@ -375,6 +385,7 @@ async function inventoryChecksRoutes(fastify) {
completed_at: true,
created_at: true,
updated_at: true,
created_by: true,
items: {
orderBy: [{ asset: { asset_no: "asc" } }],
select: {
@@ -505,6 +516,7 @@ async function inventoryChecksRoutes(fastify) {
},
async (request, reply) => {
const inventoryCheckId = parseUuid("id", request.params?.id);
const emailTasks = [];
const inventoryCheck = await fastify.prisma.$transaction(async (tx) => {
const current = await ensureInventoryCheck(tx, inventoryCheckId);
ensureCheckStatus(current, "counting");
@@ -526,6 +538,7 @@ async function inventoryChecksRoutes(fastify) {
completed_at: true,
created_at: true,
updated_at: true,
created_by: true,
items: {
orderBy: [{ asset: { asset_no: "asc" } }],
select: {
@@ -563,9 +576,56 @@ async function inventoryChecksRoutes(fastify) {
}),
});
const financeMgrRecipients = await resolveRecipientsForAssignee(tx, { assigneeRole: "finance_mgr" });
const recipients = uniq([updated.created_by, ...financeMgrRecipients]);
const title = `【盘点完成】盘点单 ${updated.check_no}`;
const bodyText = `盘点单 ${updated.check_no} 已完成,请在资产盘点中查看结果。`;
await createNotifications(tx, {
recipientEmployeeIds: recipients,
type: NOTIFICATION_TYPES.inventoryCheckCompleted,
title,
body: bodyText,
targetType: "inventory_check",
targetId: updated.id,
});
await enqueueImNotifications(tx, {
config: fastify.config,
logger: fastify.log,
recipientEmployeeIds: recipients,
type: NOTIFICATION_TYPES.inventoryCheckCompleted,
title,
body: bodyText,
targetType: "inventory_check",
targetId: updated.id,
targetPath: "/inventory-checks",
entityType: "inventory_check",
entityId: updated.id,
actorId: request.employee.id,
});
emailTasks.push({
type: NOTIFICATION_TYPES.inventoryCheckCompleted,
recipientEmployeeIds: recipients,
subject: title,
body: bodyText,
targetPath: "/inventory-checks",
});
return updated;
});
for (const task of emailTasks) {
void sendNotificationEmailSafe({
prisma: fastify.prisma,
config: fastify.config,
logger: fastify.log,
requestId: request.id,
...task,
});
}
return sendData(reply, request, { inventoryCheck: mapInventoryCheck(inventoryCheck) });
},
);