TTeamDesk 学习手册

src/server/jobs/outbox.ts

此代码来自构建时的仓库快照。修改源码后重新构建即可更新。

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,
    },
  });
}

搜索教程

输入关键词,搜索全部章节。

阅读设置

外观
正文字号

从一张工单出发,读懂数据如何流转。

设置与进度仅保存在当前浏览器。