完善接单平台通知配置
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
import {
|
||||
bumpAdminNotificationReminder,
|
||||
countPendingAdminNotifications,
|
||||
createAdminNotification,
|
||||
getPendingAdminNotificationByDedupeKey,
|
||||
listPendingAdminNotifications,
|
||||
resolveAdminNotificationsByEntity,
|
||||
type AdminNotificationRow,
|
||||
} from '../../repositories/admin-notification-repo.js'
|
||||
import { notifyInternalSafely } from '../notification/domain-notifications.js'
|
||||
import {
|
||||
getWorkerPlatformNotificationConfig,
|
||||
type WorkerPlatformNotificationEventKey,
|
||||
} from '../worker-platform/worker-platform-notification-config-service.js'
|
||||
|
||||
const ADMIN_WORKER_PLATFORM_PATH = '/admin/worker-platform'
|
||||
|
||||
export async function createWorkerWithdrawAdminNotification(input: {
|
||||
requestId: number
|
||||
workerName: string
|
||||
amountFen: number
|
||||
channel: string
|
||||
}) {
|
||||
return createConfiguredWorkerPlatformNotification('withdraw_requested', {
|
||||
notificationType: 'worker_withdraw_requested',
|
||||
priority: 'high',
|
||||
entityType: 'worker_finance_request',
|
||||
entityId: input.requestId,
|
||||
dedupeKey: `worker_withdraw_request:${input.requestId}`,
|
||||
title: '有新的提现申请',
|
||||
body: `打手:${input.workerName || '-'};金额:${formatAmount(input.amountFen)};渠道:${formatChannel(input.channel)}`,
|
||||
actionPath: `${ADMIN_WORKER_PLATFORM_PATH}?tab=finance`,
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function createWorkerRechargeAdminNotification(input: {
|
||||
requestId: number
|
||||
workerName: string
|
||||
amountFen: number
|
||||
}) {
|
||||
return createConfiguredWorkerPlatformNotification('recharge_requested', {
|
||||
notificationType: 'worker_recharge_requested',
|
||||
priority: 'normal',
|
||||
entityType: 'worker_finance_request',
|
||||
entityId: input.requestId,
|
||||
dedupeKey: `worker_recharge_request:${input.requestId}`,
|
||||
title: '有新的充值申请',
|
||||
body: `打手:${input.workerName || '-'};金额:${formatAmount(input.amountFen)}`,
|
||||
actionPath: `${ADMIN_WORKER_PLATFORM_PATH}?tab=finance`,
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function createWorkOrderMaterialAdminNotification(input: {
|
||||
workOrderId: number
|
||||
workOrderNo: string
|
||||
productName: string
|
||||
}) {
|
||||
return createConfiguredWorkerPlatformNotification('material_required', {
|
||||
notificationType: 'work_order_material_required',
|
||||
priority: 'normal',
|
||||
entityType: 'work_order',
|
||||
entityId: input.workOrderId,
|
||||
dedupeKey: `work_order_material:${input.workOrderId}`,
|
||||
title: '有工单待补资料',
|
||||
body: `订单:${input.workOrderNo || input.workOrderId};商品:${input.productName || '-'}`,
|
||||
actionPath: `${ADMIN_WORKER_PLATFORM_PATH}?tab=orders`,
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function createWorkerAcceptanceAdminNotification(input: {
|
||||
workOrderId: number
|
||||
workOrderNo: string
|
||||
productName: string
|
||||
workerName: string
|
||||
imageCount: number
|
||||
}) {
|
||||
return createConfiguredWorkerPlatformNotification('acceptance_submitted', {
|
||||
notificationType: 'worker_acceptance_submitted',
|
||||
priority: 'normal',
|
||||
entityType: 'work_order',
|
||||
entityId: input.workOrderId,
|
||||
dedupeKey: acceptanceNotificationKey(input.workOrderId),
|
||||
title: '有订单待验收',
|
||||
body: [
|
||||
`订单:${input.workOrderNo || input.workOrderId}`,
|
||||
`商品:${input.productName || '-'}`,
|
||||
`打手:${input.workerName || '-'}`,
|
||||
`验收图片:${input.imageCount} 张`,
|
||||
].join(';'),
|
||||
actionPath: `${ADMIN_WORKER_PLATFORM_PATH}?tab=orders`,
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function remindWorkerAcceptanceAdminNotification(workOrderId: number) {
|
||||
const now = new Date().toISOString()
|
||||
const eventConfig = getWorkerPlatformNotificationConfig().events.acceptance_reminded
|
||||
const notification = await bumpAdminNotificationReminder({
|
||||
dedupeKey: acceptanceNotificationKey(workOrderId),
|
||||
notificationType: 'worker_acceptance_reminded',
|
||||
title: '有打手催验收',
|
||||
visible: eventConfig.todoEnabled,
|
||||
soundEnabled: eventConfig.todoEnabled && eventConfig.soundEnabled,
|
||||
now,
|
||||
})
|
||||
if (notification && eventConfig.externalPushEnabled) {
|
||||
void notifyInternalSafely({
|
||||
title: '打手催促验收',
|
||||
body: `${notification.body};第 ${notification.reminder_count} 次催验收`,
|
||||
category: 'worker_acceptance_reminded',
|
||||
url: notification.action_path,
|
||||
cooldownKey: `worker_acceptance_reminded:${workOrderId}:${notification.reminder_count}`,
|
||||
cooldownMs: 30 * 60 * 1000,
|
||||
})
|
||||
}
|
||||
return notification
|
||||
}
|
||||
|
||||
export async function getWorkerAcceptanceReminder(workOrderId: number) {
|
||||
return getPendingAdminNotificationByDedupeKey(acceptanceNotificationKey(workOrderId))
|
||||
}
|
||||
|
||||
export async function resolveAdminNotificationEntity(
|
||||
entityType: string,
|
||||
entityId: number,
|
||||
reason: string,
|
||||
) {
|
||||
await resolveAdminNotificationsByEntity({
|
||||
entityType,
|
||||
entityId,
|
||||
reason,
|
||||
now: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
export async function listAdminPendingNotifications(limit = 20) {
|
||||
const normalizedLimit = Math.min(50, Math.max(1, Math.floor(Number(limit) || 20)))
|
||||
const [items, pendingCount] = await Promise.all([
|
||||
listPendingAdminNotifications(normalizedLimit),
|
||||
countPendingAdminNotifications(),
|
||||
])
|
||||
return {
|
||||
pendingCount,
|
||||
items: items.map(mapAdminNotification),
|
||||
}
|
||||
}
|
||||
|
||||
function mapAdminNotification(row: AdminNotificationRow) {
|
||||
return {
|
||||
notificationId: Number(row.id),
|
||||
notificationType: row.notification_type,
|
||||
priority: row.priority,
|
||||
entityType: row.entity_type,
|
||||
entityId: Number(row.entity_id),
|
||||
title: row.title,
|
||||
body: row.body,
|
||||
actionPath: row.action_path,
|
||||
reminderCount: Number(row.reminder_count || 0),
|
||||
soundEnabled: row.sound_enabled !== false,
|
||||
lastRemindedAt: row.last_reminded_at,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
async function createConfiguredWorkerPlatformNotification(
|
||||
eventKey: WorkerPlatformNotificationEventKey,
|
||||
input: {
|
||||
notificationType: string
|
||||
priority: string
|
||||
entityType: string
|
||||
entityId: number
|
||||
dedupeKey: string
|
||||
title: string
|
||||
body: string
|
||||
actionPath: string
|
||||
now: string
|
||||
},
|
||||
) {
|
||||
const eventConfig = getWorkerPlatformNotificationConfig().events[eventKey]
|
||||
const notification = await createAdminNotification({
|
||||
...input,
|
||||
visible: eventConfig.todoEnabled,
|
||||
soundEnabled: eventConfig.todoEnabled && eventConfig.soundEnabled,
|
||||
})
|
||||
if (notification && eventConfig.externalPushEnabled) {
|
||||
void notifyInternalSafely({
|
||||
title: notification.title,
|
||||
body: notification.body,
|
||||
category: notification.notification_type,
|
||||
url: notification.action_path,
|
||||
cooldownKey: notification.dedupe_key,
|
||||
})
|
||||
}
|
||||
return notification
|
||||
}
|
||||
|
||||
function acceptanceNotificationKey(workOrderId: number) {
|
||||
return `work_order_acceptance:${workOrderId}`
|
||||
}
|
||||
|
||||
function formatAmount(amountFen: number) {
|
||||
return `¥${(Math.max(0, Number(amountFen) || 0) / 100).toFixed(2)}`
|
||||
}
|
||||
|
||||
function formatChannel(channel: string) {
|
||||
return channel === 'wechat' ? '微信' : channel === 'alipay' ? '支付宝' : channel || '-'
|
||||
}
|
||||
Reference in New Issue
Block a user