Files
iistwin/server/storage/bots.storage.ts
Ильяс Султанов 1f5ecb6da4 fix(number-fields): избегаем потери точности длинных чисел
- number-поля теперь рендерятся как text + inputMode=numeric,
  чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage

Closes: искажение расчётного счёта и других длинных числовых полей
2026-07-07 21:03:40 +03:00

186 lines
6.1 KiB
TypeScript

import { bots, botSubscriptions, botSessions, type Bot, type BotSubscription, type BotSession, type InsertBot, type InsertBotSubscription, type InsertBotSession, type BotWithSubscriptions } from "@shared/schema";
import { db } from "../db";
import { eq, and, desc, ilike, or } from "drizzle-orm";
import crypto from "crypto";
import { SocialStorage } from "./social.storage";
export class BotsStorage extends SocialStorage {
async getBot(id: number, organizationId: number): Promise<Bot | undefined> {
const [bot] = await db
.select()
.from(bots)
.where(and(eq(bots.id, id), eq(bots.organizationId, organizationId)));
return bot || undefined;
}
async getBotByLogin(login: string, organizationId: number): Promise<Bot | undefined> {
const [bot] = await db
.select()
.from(bots)
.where(and(eq(bots.login, login), eq(bots.organizationId, organizationId)));
return bot || undefined;
}
async getBotsByOrganization(organizationId: number): Promise<Bot[]> {
return await db
.select()
.from(bots)
.where(eq(bots.organizationId, organizationId))
.orderBy(desc(bots.createdAt));
}
async getBotWithSubscriptions(id: number, organizationId: number): Promise<BotWithSubscriptions | undefined> {
const [bot] = await db
.select()
.from(bots)
.where(and(eq(bots.id, id), eq(bots.organizationId, organizationId)));
if (!bot) return undefined;
const subscriptionsList = await db
.select()
.from(botSubscriptions)
.where(and(eq(botSubscriptions.botId, id), eq(botSubscriptions.organizationId, organizationId)));
return {
...bot,
subscriptions: subscriptionsList,
};
}
async createBot(insertBot: InsertBot): Promise<Bot> {
const [bot] = await db.insert(bots).values(insertBot).returning();
return bot;
}
async updateBot(id: number, organizationId: number, updates: Partial<Bot>): Promise<Bot> {
const [bot] = await db
.update(bots)
.set({ ...updates, updatedAt: new Date() })
.where(and(eq(bots.id, id), eq(bots.organizationId, organizationId)))
.returning();
return bot;
}
async deleteBot(id: number, organizationId: number): Promise<void> {
await db
.delete(bots)
.where(and(eq(bots.id, id), eq(bots.organizationId, organizationId)));
}
async searchBots(query: string, organizationId: number, limit: number = 10): Promise<Bot[]> {
const searchPattern = `%${query.toLowerCase()}%`;
return await db
.select()
.from(bots)
.where(and(
eq(bots.organizationId, organizationId),
eq(bots.isActive, true),
or(
ilike(bots.name, searchPattern),
ilike(bots.login, searchPattern)
)
))
.limit(limit);
}
// =====================================================
// BOT SUBSCRIPTIONS
// =====================================================
async getBotSubscriptions(botId: number, organizationId: number): Promise<BotSubscription[]> {
return await db
.select()
.from(botSubscriptions)
.where(and(eq(botSubscriptions.botId, botId), eq(botSubscriptions.organizationId, organizationId)));
}
async getBotSubscriptionsByForm(formId: number, organizationId: number): Promise<BotSubscription[]> {
return await db
.select()
.from(botSubscriptions)
.where(and(
eq(botSubscriptions.formId, formId),
eq(botSubscriptions.organizationId, organizationId),
eq(botSubscriptions.isActive, true)
));
}
async getBotSubscriptionsByStatus(statusId: number, organizationId: number): Promise<BotSubscription[]> {
return await db
.select()
.from(botSubscriptions)
.where(and(
eq(botSubscriptions.statusId, statusId),
eq(botSubscriptions.organizationId, organizationId),
eq(botSubscriptions.isActive, true)
));
}
async getBotSubscriptionsByTransition(transitionId: number, organizationId: number): Promise<BotSubscription[]> {
return await db
.select()
.from(botSubscriptions)
.where(and(
eq(botSubscriptions.transitionId, transitionId),
eq(botSubscriptions.organizationId, organizationId),
eq(botSubscriptions.isActive, true)
));
}
async createBotSubscription(insertSubscription: InsertBotSubscription): Promise<BotSubscription> {
const [subscription] = await db
.insert(botSubscriptions)
.values(insertSubscription)
.returning();
return subscription;
}
async updateBotSubscription(id: number, organizationId: number, updates: Partial<BotSubscription>): Promise<BotSubscription> {
const [subscription] = await db
.update(botSubscriptions)
.set(updates)
.where(and(eq(botSubscriptions.id, id), eq(botSubscriptions.organizationId, organizationId)))
.returning();
return subscription;
}
async deleteBotSubscription(id: number, organizationId: number): Promise<void> {
await db
.delete(botSubscriptions)
.where(and(eq(botSubscriptions.id, id), eq(botSubscriptions.organizationId, organizationId)));
}
// =====================================================
// BOT SESSIONS
// =====================================================
async createBotSession(session: InsertBotSession): Promise<BotSession> {
const [botSession] = await db
.insert(botSessions)
.values(session)
.returning();
return botSession;
}
async getBotSessionByToken(refreshToken: string): Promise<BotSession | undefined> {
const [session] = await db
.select()
.from(botSessions)
.where(eq(botSessions.refreshToken, refreshToken));
return session || undefined;
}
async deleteBotSession(refreshToken: string): Promise<void> {
await db.delete(botSessions).where(eq(botSessions.refreshToken, refreshToken));
}
async deleteBotSessions(botId: number): Promise<void> {
await db.delete(botSessions).where(eq(botSessions.botId, botId));
}
// =====================================================
// DATA TABLES
// =====================================================
}