feat(automations): ctx.tasks.scheduleStatusChange — отложенная смена статуса (воркер 5с, отмена при ручном переходе)

This commit is contained in:
2026-09-17 16:38:56 +03:00
parent c1fe1c4e02
commit e4f9a1992a
9 changed files with 236 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
import { users, organizations, forms, tasks, safeUserColumns, type User, type SafeUser, type Organization, type Task } from "@shared/schema";
import { taskRelations, type TaskRelation } from "@shared/schema";
import { taskReminders, type TaskReminder, type InsertTaskReminder } from "@shared/schema";
import { delayedStatusChanges, type DelayedStatusChange, type InsertDelayedStatusChange } from "@shared/schema";
import { taskAuditLog, type TaskAuditLog, type InsertTaskAuditLog } from "@shared/schema";
import { roleMembers, roles } from "@shared/schema";
import { db } from "../db";
@@ -96,6 +97,31 @@ export class TaskMetaStorage extends ContentStorage {
.where(eq(taskReminders.id, id));
}
// Отложенная смена статуса (автоматизации: ctx.tasks.scheduleStatusChange)
async createDelayedStatusChange(data: InsertDelayedStatusChange): Promise<DelayedStatusChange> {
const [row] = await db.insert(delayedStatusChanges).values(data).returning();
return row;
}
async getDueDelayedStatusChanges(limit = 50): Promise<DelayedStatusChange[]> {
return db
.select()
.from(delayedStatusChanges)
.where(and(
sql`${delayedStatusChanges.executedAt} IS NULL`,
lte(delayedStatusChanges.runAt, new Date()),
))
.orderBy(asc(delayedStatusChanges.runAt))
.limit(limit);
}
async markDelayedStatusChangeExecuted(id: number): Promise<void> {
await db
.update(delayedStatusChanges)
.set({ executedAt: new Date() })
.where(eq(delayedStatusChanges.id, id));
}
// Массовые выборки пользователей — только безопасные колонки (без хэша пароля и токенов)
async getUsersByRole(role: string, organizationId: number): Promise<SafeUser[]> {
return db