src/server/jobs/outbox.ts
此代码来自构建时的仓库快照。修改源码后重新构建即可更新。
import type { Prisma, TicketPriority } from '@/generated/prisma/client';
const slaHours: Record<TicketPriority, number> = {
URGENT: 1,
HIGH: 4,
NORMAL: 24,
LOW: 48,
};
export function defaultDueAt(priority: TicketPriority, now = new Date()) {
return new Date(now.getTime() + slaHours[priority] * 3600000);
}
export async function invalidateReminders(
tx: Prisma.TransactionClient,
teamId: string,
ticketId: string,
) {
// 立即结束旧待办,页面不会继续把已失效的延迟提醒显示为等待执行。
// Redis 中尚未到期的旧 job 即使后来被唤醒,也会看到已完成状态并直接跳过。
await tx.backgroundTask.updateMany({
where: {
teamId,
ticketId,
kind: 'REMINDER',
status: { in: ['WAITING', 'RUNNING'] },
},
data: {
status: 'COMPLETED',
progress: 100,
executionId: null,
resultSummary: '已跳过:提醒已取消、被新设置替代或工单已解决',
},
});
}
export async function scheduleReminder(
tx: Prisma.TransactionClient,
ticket: {
id: string;
teamId: string;
creatorId: string | null;
dueAt: Date | null;
reminderRevision: number;
},
) {
await invalidateReminders(tx, ticket.teamId, ticket.id);
if (!ticket.dueAt) return;
// 和工单在同一事务写入待投递箱;此处不访问 Redis,避免数据库已提交却入队失败的“丢任务”。
await tx.backgroundTask.create({
data: {
teamId: ticket.teamId,
ticketId: ticket.id,
ownerId: ticket.creatorId,
kind: 'REMINDER',
reminderRevision: ticket.reminderRevision,
runAt: ticket.dueAt,
},
});
}