R50 通知推送:dispatcher 投递服务、业务事件接入与员工推送开关
这个提交包含在:
@@ -0,0 +1,38 @@
|
||||
const { signPayload } = require('../../utils/signedUrl');
|
||||
|
||||
function resolveBaseUrl(config) {
|
||||
const origin = String(config?.appOrigin ?? '').split(',')[0]?.trim?.() ?? '';
|
||||
return origin ? origin.replace(/\/+$/, '') : '';
|
||||
}
|
||||
|
||||
function buildDetailUrl(config, targetPath) {
|
||||
const path = String(targetPath ?? '').trim();
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const baseUrl = resolveBaseUrl(config);
|
||||
if (!baseUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (path.startsWith('http://') || path.startsWith('https://')) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
||||
const token = signPayload(
|
||||
{
|
||||
targetPath: normalizedPath,
|
||||
exp: Math.floor(Date.now() / 1000) + 15 * 60,
|
||||
},
|
||||
config.jwtSecret,
|
||||
);
|
||||
|
||||
return `${baseUrl}/api/auth/deeplink?token=${encodeURIComponent(token)}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDetailUrl,
|
||||
resolveBaseUrl,
|
||||
};
|
||||
@@ -7,44 +7,12 @@ const {
|
||||
matchesNotificationChannel,
|
||||
normalizeChannelType,
|
||||
} = require('./imRouting');
|
||||
const { signPayload } = require('../../utils/signedUrl');
|
||||
const { buildDetailUrl } = require('./detailUrl');
|
||||
|
||||
function uniq(values) {
|
||||
return Array.from(new Set((values ?? []).filter(Boolean)));
|
||||
}
|
||||
|
||||
function resolveBaseUrl(config) {
|
||||
const origin = String(config?.appOrigin ?? '').split(',')[0]?.trim?.() ?? '';
|
||||
return origin ? origin.replace(/\/+$/, '') : '';
|
||||
}
|
||||
|
||||
function buildDetailUrl(config, targetPath) {
|
||||
const path = String(targetPath ?? '').trim();
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const baseUrl = resolveBaseUrl(config);
|
||||
if (!baseUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (path.startsWith('http://') || path.startsWith('https://')) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
||||
const token = signPayload(
|
||||
{
|
||||
targetPath: normalizedPath,
|
||||
exp: Math.floor(Date.now() / 1000) + 15 * 60,
|
||||
},
|
||||
config.jwtSecret,
|
||||
);
|
||||
|
||||
return `${baseUrl}/api/auth/deeplink?token=${encodeURIComponent(token)}`;
|
||||
}
|
||||
|
||||
async function resolveRecipientRoleCodes(tx, recipientEmployeeIds) {
|
||||
const recipientIds = uniq(recipientEmployeeIds);
|
||||
if (!recipientIds.length) {
|
||||
|
||||
@@ -49,8 +49,38 @@ async function createNotifications(tx, input) {
|
||||
});
|
||||
}
|
||||
|
||||
async function createNotificationsWithRecords(tx, input) {
|
||||
const recipientIds = uniq(input.recipientEmployeeIds);
|
||||
if (recipientIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const title = String(input.title ?? '').slice(0, 200);
|
||||
const body = input.body != null ? String(input.body).slice(0, 5000) : null;
|
||||
const type = String(input.type ?? '').slice(0, 64);
|
||||
const targetType = input.targetType != null ? String(input.targetType).slice(0, 64) : null;
|
||||
|
||||
const created = [];
|
||||
for (const recipientId of recipientIds) {
|
||||
created.push(
|
||||
await tx.notification.create({
|
||||
data: {
|
||||
recipient_employee_id: recipientId,
|
||||
type,
|
||||
title,
|
||||
body,
|
||||
target_type: targetType,
|
||||
target_id: input.targetId ?? null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createNotifications,
|
||||
createNotificationsWithRecords,
|
||||
resolveRecipientsForAssignee,
|
||||
};
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户