feat(reminders): получатели в отложенных/входящих, приватные сообщения чата, календарь, бейдж уведомлений

This commit is contained in:
2026-09-21 10:26:53 +03:00
parent 17e6ebf412
commit aa316141be
20 changed files with 312 additions and 36 deletions

View File

@@ -267,6 +267,18 @@ export class SocialStorage extends TasksStorage {
}
// User Notifications
async getUnreadNotificationCount(userId: number, organizationId: number): Promise<number> {
const [row] = await db
.select({ count: sql<number>`COUNT(*)::int` })
.from(userNotifications)
.where(and(
eq(userNotifications.userId, userId),
eq(userNotifications.organizationId, organizationId),
eq(userNotifications.isRead, false),
));
return row?.count ?? 0;
}
async getUserNotifications(userId: number, organizationId: number, isRead?: boolean): Promise<UserNotification[]> {
const conditions = [
eq(userNotifications.userId, userId),

View File

@@ -2,7 +2,7 @@ import { users, forms, tasks, taskMessages, messageReads, bots, roles, taskViews
import { taskRelations } from "@shared/schema";
import { taskAssignees, type TaskAssignee } from "@shared/schema";
import { db } from "../db";
import { eq, and, desc, exists, sql, gt, inArray } from "drizzle-orm";
import { eq, and, desc, exists, sql, gt, inArray, isNull, or } from "drizzle-orm";
import { formatUserName } from "../utils/formatUserName";
import { TasksCoreStorage } from "./tasks-core.storage";
import { fieldConditionsMatch, resolveFieldDisplayText } from "../utils/field-conditions";
@@ -239,7 +239,7 @@ export class TasksStorage extends TasksCoreStorage {
}
// Task Messages (Chat)
async getTaskMessages(taskId: number, organizationId: number, afterId?: number): Promise<TaskMessageWithAuthor[]> {
async getTaskMessages(taskId: number, organizationId: number, afterId?: number, viewerUserId?: number): Promise<TaskMessageWithAuthor[]> {
const replyToMsg = db.select({
id: taskMessages.id,
message: taskMessages.message,
@@ -290,7 +290,15 @@ export class TasksStorage extends TasksCoreStorage {
.where(and(
eq(taskMessages.taskId, taskId),
eq(forms.organizationId, organizationId),
...(afterId ? [gt(taskMessages.id, afterId)] : [])
...(afterId ? [gt(taskMessages.id, afterId)] : []),
// Приватные сообщения (visible_to_user_ids) — только для перечисленных пользователей.
// Вызовы без viewerUserId (AI-бот, MCP, внутренние) получают полную ленту.
...(viewerUserId
? [or(
isNull(taskMessages.visibleToUserIds),
sql`${taskMessages.visibleToUserIds} @> jsonb_build_array(${viewerUserId}::int)`
)]
: [])
))
.orderBy(taskMessages.createdAt);