Files
MCAdroidApp/CHAT_API_DOCUMENTATION.md
Viktoria Polyakova 9f8b968452 Updated
2026-01-30 16:58:30 +03:00

39 KiB
Raw Blame History

Chat API Documentation

Обзор

Система чата является комплексным решением для обмена сообщениями в CRM системе, поддерживающим множественные провайдеры (WhatsApp, Telegram, Messenger и др.), персональные и групповые чаты, а также интеграцию с сущностями системы.

Архитектура

Компоненты системы

  1. Backend (NestJS)

    • REST API для управления чатами
    • WebSocket для real-time сообщений
    • Интеграция с внешними провайдерами
  2. Frontend (React/TypeScript)

    • Веб-интерфейс для управления чатами
    • Модальные окна и встроенные компоненты
  3. Android приложение

    • Мобильный клиент для Android устройств
    • Полная функциональность чата
  4. База данных

    • PostgreSQL с TypeORM
    • Комплексная схема для чатов, сообщений, пользователей

Backend API

Детальная спецификация API

MultichatController (/chat)

Основной контроллер для общих операций с чатами.

@ApiTags('multichat')
@Controller('chat')
@JwtAuthorized()
export class MultichatController {
  // Получение количества непрочитанных сообщений
  @Get('unseen-count')
  async getUnseenCount(): Promise<number>

  // Обновление статуса сообщений для всех чатов
  @Put('status/:status')
  async updateMessagesStatus()
}
GET /chat/unseen-count

Описание: Получить общее количество непрочитанных сообщений для текущего пользователя.

Аутентификация: JWT токен обязательна

Параметры: Нет

Ответ:

{
  "statusCode": 200,
  "data": 5
}
PUT /chat/status/{status}

Описание: Обновить статус всех сообщений во всех чатах пользователя.

Аутентификация: JWT токен обязательна

Параметры пути:

  • status (string): Новый статус (received/seen)

Тело запроса:

{
  "accountId": 1,
  "user": {...},
  "status": "seen"
}

Ответ:

{
  "statusCode": 200,
  "message": "Messages status updated"
}

ChatController (/chat/chats)

Контроллер для операций с конкретными чатами.

Создание чатов:

  • POST /personal - персональный чат
  • POST /group - групповой чат
  • POST /external - внешний чат

Получение чатов:

  • GET / - список чатов с пагинацией
  • GET /find - простой поиск
  • GET /find/full - полный поиск с дополнительными данными
  • GET /find/full/personal - поиск по имени пользователя
  • GET /find/full/by-message-content - поиск по содержимому сообщений
  • GET /:chatId - получение конкретного чата

Управление чатами:

  • PATCH /group/:chatId - обновление группового чата
  • DELETE /:chatId - удаление чата
  • PUT /:chatId/pin/:messageId - закрепление сообщения
  • PUT /:chatId/unpin/:messageId - открепление сообщения
  • PUT /:chatId/status/:status - обновление статуса сообщений в чате

Дополнительные операции:

  • POST /:chatId/contact - создание контакта и лида

ChatMessageController (/chat/chats/:chatId/messages)

Контроллер для работы с сообщениями в чате.

Операции с сообщениями:

  • POST / - отправка сообщения
  • GET / - получение списка сообщений
  • GET /:messageId - получение конкретного сообщения
  • PUT /:messageId - редактирование сообщения
  • DELETE /:messageId - удаление сообщения

Статусы сообщений:

  • POST /status/:status - обновление статуса нескольких сообщений
  • PUT /:messageId/status/:status - обновление статуса одного сообщения

Реакции:

  • PUT /:messageId/react/:reaction - добавление реакции
  • PUT /:messageId/unreact/:reactionId - удаление реакции

ChatProviderController (/chat/providers)

Контроллер для работы с провайдерами чатов.

@ApiTags('multichat/providers')
@Controller('chat/providers')
@JwtAuthorized()
export class ChatProviderController {
  @Get()
  async findMany(): Promise<ChatProviderDto[]>
}

Модели данных

Chat (Чат)

export class ChatDto {
  id: number;                    // ID чата
  providerId: number;           // ID провайдера
  createdBy: number;            // ID создателя
  externalId: string | null;    // Внешний ID
  type: ChatType;               // Тип чата (PERSONAL/GROUP)
  title: string | null;         // Название
  entityId: number | null;      // ID связанной сущности
  createdAt: string;            // Дата создания
  users: ChatUserDto[];         // Пользователи чата
  pinnedMessages: ChatMessageDto[]; // Закрепленные сообщения
  lastMessage: ChatMessageDto | null; // Последнее сообщение
  unseenCount: number;          // Количество непрочитанных
  updatedAt: string;            // Дата обновления
  entityInfo: EntityInfoDto | null; // Информация о сущности
  hasAccess?: boolean | null;   // Доступ пользователя
}

ChatMessage (Сообщение)

export class ChatMessageDto {
  id: number;                    // ID сообщения
  chatId: number;               // ID чата
  chatUserId: number;           // ID пользователя чата
  text: string | null;          // Текст сообщения
  replyToId: number | null;     // ID сообщения-ответа
  createdAt: string;            // Дата создания
  files: ChatMessageFileDto[];  // Прикрепленные файлы
  reactions: ChatMessageReactionDto[]; // Реакции
  status: ChatMessageStatus;    // Статус для текущего пользователя
}

ChatUser (Пользователь чата)

export class ChatUserDto {
  id: number;                    // ID в чате
  chatId: number;               // ID чата
  userId: number | null;        // ID пользователя системы
  role: ChatUserRole;           // Роль в чате
  name: string | null;          // Имя
  avatarUrl: string | null;     // URL аватара
}

ChatProvider (Провайдер)

export class ChatProviderDto {
  id: number;                    // ID провайдера
  type: ChatProviderType;       // Тип провайдера
  title: string;                // Название
  status: ChatProviderStatus;   // Статус
  transport: ChatProviderTransport; // Транспорт
  unseenCount: number;          // Непрочитанные сообщения
}

Статусы и типы

ChatType

export enum ChatType {
  PERSONAL = 'personal',  // Персональный чат между двумя пользователями
  GROUP = 'group'         // Групповой чат с множеством участников
}

ChatMessageStatus

export enum ChatMessageStatus {
  RECEIVED = 'received',  // Сообщение получено
  SEEN = 'seen'           // Сообщение просмотрено
}

ChatProviderType

export enum ChatProviderType {
  Amwork = 'amwork',      // Внутренняя система Amwork
  Twilio = 'twilio',     // Twilio WhatsApp
  Facebook = 'facebook', // Facebook Messenger
  Wazzup = 'wazzup'      // Wazzup WhatsApp
}

ChatProviderTransport

export enum ChatProviderTransport {
  Amwork = 'amwork',      // Внутренняя система
  Whatsapp = 'whatsapp', // WhatsApp
  Messenger = 'messenger', // Facebook Messenger
  Telegram = 'telegram', // Telegram
  Instagram = 'instagram', // Instagram
  Vk = 'vk',             // VKontakte
  Avito = 'avito'        // Avito
}

ChatProviderStatus

export enum ChatProviderStatus {
  Draft = 'draft',       // Черновик настройки
  Active = 'active',     // Активен
  Inactive = 'inactive', // Неактивен
  Deleted = 'deleted'    // Удален
}

ChatUserRole

export enum ChatUserRole {
  OWNER = 'owner',       // Владелец чата
  ADMIN = 'admin',       // Администратор
  SUPERVISOR = 'supervisor', // Супервайзер
  USER = 'user',         // Обычный пользователь
  EXTERNAL = 'external'  // Внешний пользователь
}

DTOs для создания чатов

CreatePersonalChatDto

export class CreatePersonalChatDto {
  @ApiProperty({ description: 'Provider ID' })
  @IsNumber()
  providerId: number;

  @ApiProperty({ description: 'User ID of chat companion' })
  @IsNumber()
  companionId: number;
}

Пример запроса:

{
  "providerId": 1,
  "companionId": 123
}

CreateGroupChatDto

export class CreateGroupChatDto {
  @ApiProperty({ description: 'Provider ID' })
  @IsNumber()
  providerId: number;

  @ApiProperty({ description: 'Chat title' })
  @IsString()
  title: string;

  @ApiProperty({ description: 'User IDs of chat participants', type: [Number] })
  @IsArray()
  participantIds: number[];

  @ApiPropertyOptional({ description: 'Entity ID associated with the chat', nullable: true })
  @IsOptional()
  @IsNumber()
  entityId?: number | null;
}

Пример запроса:

{
  "providerId": 1,
  "title": "Проект Alpha",
  "participantIds": [123, 456, 789],
  "entityId": 1001
}

DTOs для сообщений

SendChatMessageDto

export class SendChatMessageDto {
  @ApiPropertyOptional()
  @IsOptional()
  @IsNumber()
  replyToId?: number | null;

  @ApiProperty()
  @IsString()
  text: string;

  @ApiPropertyOptional({ nullable: true })
  @IsOptional()
  @IsArray()
  fileIds?: string[] | null;
}

Пример запроса:

{
  "text": "Привет, как дела?",
  "replyToId": 123,
  "fileIds": ["file_123", "file_456"]
}

База данных

Основные таблицы

chat

CREATE TABLE public.chat (
    id integer NOT NULL,
    provider_id integer NOT NULL,
    created_by integer,
    external_id character varying,
    type character varying NOT NULL,
    title character varying,
    entity_id integer,
    account_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL
);

chat_message

CREATE TABLE public.chat_message (
    id integer NOT NULL,
    chat_id integer NOT NULL,
    chat_user_id integer NOT NULL,
    external_id character varying,
    text text,
    account_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL,
    reply_to_id integer
);

chat_user

CREATE TABLE public.chat_user (
    id integer NOT NULL,
    chat_id integer NOT NULL,
    user_id integer,
    role character varying NOT NULL,
    account_id integer NOT NULL
);

chat_provider

CREATE TABLE public.chat_provider (
    id integer NOT NULL,
    created_by integer,
    type character varying NOT NULL,
    title character varying NOT NULL,
    account_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL,
    status character varying NOT NULL,
    transport character varying NOT NULL
);

chat_message_reaction

CREATE TABLE public.chat_message_reaction (
    id integer NOT NULL,
    message_id integer NOT NULL,
    chat_user_id integer NOT NULL,
    reaction character varying NOT NULL,
    account_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL
);

chat_message_user_status

CREATE TABLE public.chat_message_user_status (
    chat_id integer NOT NULL,
    message_id integer NOT NULL,
    chat_user_id integer NOT NULL,
    status character varying NOT NULL,
    account_id integer NOT NULL,
    created_at timestamp without time zone NOT NULL
);

Frontend

Архитектура компонентов

Система чата построена на базе React с использованием контекстов для управления состоянием и кастомных хуков для работы с API.

MultichatProvider

Контекст-провайдер для глобального управления состоянием чата в приложении.

Ключевые свойства:

interface MultichatContextValue {
  // Видимость модальных окон
  opened: boolean;
  pageOpened: boolean;
  hide: () => void;
  toggle: () => void;

  // Активный чат
  activeChatId: number | null;
  activeProviderId: number | null;
  setActiveChatId: (id: number | null) => void;
  setActiveProviderId: (id: number | null) => void;

  // Управление сообщениями
  editMessageId: number | null;
  replyToId: number | null;
  setEditMessageId: (id: number | null) => void;
  setReplyToId: (id: number | null) => void;

  // Навигация
  show: (params: { activeChatId?: number; activeProviderId?: number }) => void;
  openPage: (returnUrl?: string) => void;
  closePage: () => void;
}

Использование:

const { show, opened, activeChatId } = useMultichatContext();

// Открыть чат
show({ activeChatId: 123, activeProviderId: 1 });

// Проверить видимость
if (opened) {
  // Показать модальное окно
}

MultichatModal

Модальное окно для отображения интерфейса чата в модальном режиме.

Особенности:

  • Перетаскиваемое и изменяемое по размеру
  • Адаптивный дизайн
  • Сохранение позиции и размера
  • Интеграция с основным UI

Props:

interface MultichatModalProps {
  children?: ReactNode;
  modalBounds?: DraggableResizableControlBounds;
  setModalBounds?: (bounds: DraggableResizableControlBounds) => void;
}

MultichatControl

Основной контейнер управления чатом с боковой панелью и областью сообщений.

Структура:

MultichatControl
├── MultichatControlHeader (при модальном просмотре)
├── ChatsSidebar
│   ├── ChatList
│   ├── CreateAmworkChatButton
│   └── ProviderSelector
└── ChatMessagesPanel
    ├── ChatMessagesPanelHeader
    ├── ChatMessagesList
    └── SendChatMessageBlock

ChatMessagesPanel

Компонент для отображения и управления сообщениями в чате.

Ключевые функции:

  • Ленивая загрузка сообщений
  • Автоматическая прокрутка к новым сообщениям
  • Отметка сообщений как прочитанные
  • Обработка реакций и ответов

Props:

interface ChatMessagesPanelProps {
  activeChatId: number;
  mobileView?: boolean;
  providers?: ChatProvider[];
  activeProvider?: ChatProvider;
  modalWidth?: string;
  onBack?: () => void;
}

SendChatMessageBlock

Компонент для отправки сообщений с поддержкой различных типов контента.

Возможности:

  • Текстовые сообщения
  • Файловые вложения (drag & drop)
  • Ответы на сообщения
  • Редактирование сообщений
  • Отправка через Enter или кнопку

Состояния:

  • Обычное сообщение
  • Ответ на сообщение
  • Редактирование сообщения
  • Отправка файла

Кастомные хуки

useGetChat

Получение данных о конкретном чате.

const { data: chat, isLoading, error } = useGetChat(chatId);

useGetChatMessages

Получение сообщений чата с пагинацией.

const {
  data: messages,
  hasNextPage,
  fetchNextPage,
  isLoading
} = useGetChatMessages(chatId);

useSendChatMessage

Отправка сообщений в чат.

const { mutateAsync: sendMessage, isPending } = useSendChatMessage({
  chatId,
  providerId
});

// Отправка
await sendMessage({
  text: "Привет!",
  fileIds: ["file_123"]
});

useCreateGroupChat

Создание группового чата.

const { mutateAsync: createChat } = useCreateGroupChat();

// Создание
const newChat = await createChat({
  providerId: 1,
  title: "Новый проект",
  participantIds: [123, 456]
});

API интеграция

ChatApi

Основной API клиент для работы с чатами:

export const chatApi = {
  // Получение чатов
  getChats: (params: CursorPagingQuery) =>
    api.get('/chat/chats', { params }),

  getChat: (chatId: number) =>
    api.get(`/chat/chats/${chatId}`),

  // Создание чатов
  createPersonalChat: (data: CreatePersonalChatDto) =>
    api.post('/chat/chats/personal', data),

  createGroupChat: (data: CreateGroupChatDto) =>
    api.post('/chat/chats/group', data),

  createExternalChat: (data: CreateExternalChatDto) =>
    api.post('/chat/chats/external', data),

  // Поиск чатов
  findChats: (params: { filter: ChatFindFilterDto; paging: PagingQuery }) =>
    api.get('/chat/chats/find', { params }),

  findChatsFull: (params: { filter: ChatFindFilterDto; paging: PagingQuery }) =>
    api.get('/chat/chats/find/full', { params }),

  // Сообщения
  getChatMessages: (chatId: number, params: ChatMessagesFilterDto & PagingQuery) =>
    api.get(`/chat/chats/${chatId}/messages`, { params }),

  sendMessage: (chatId: number, data: SendChatMessageDto) =>
    api.post(`/chat/chats/${chatId}/messages`, data),

  updateMessage: (chatId: number, messageId: number, data: SendChatMessageDto) =>
    api.put(`/chat/chats/${chatId}/messages/${messageId}`, data),

  deleteMessage: (chatId: number, messageId: number) =>
    api.delete(`/chat/chats/${chatId}/messages/${messageId}`),

  // Статусы
  markAllChatMessagesAsRead: (chatId: number) =>
    api.put(`/chat/chats/${chatId}/status/read`),

  updateMessageStatus: (chatId: number, messageId: number, status: ChatMessageStatus) =>
    api.put(`/chat/chats/${chatId}/messages/${messageId}/status/${status}`),

  // Реакции
  reactToMessage: (chatId: number, messageId: number, reaction: string) =>
    api.put(`/chat/chats/${chatId}/messages/${messageId}/react/${reaction}`),

  unreactToMessage: (chatId: number, messageId: number, reactionId: number) =>
    api.put(`/chat/chats/${chatId}/messages/${messageId}/unreact/${reactionId}`),

  // Управление чатами
  updateGroupChat: (chatId: number, data: UpdateGroupChatDto) =>
    api.patch(`/chat/chats/group/${chatId}`, data),

  deleteChat: (chatId: number) =>
    api.delete(`/chat/chats/${chatId}`),

  pinMessage: (chatId: number, messageId: number) =>
    api.put(`/chat/chats/${chatId}/pin/${messageId}`),

  unpinMessage: (chatId: number, messageId: number) =>
    api.put(`/chat/chats/${chatId}/unpin/${messageId}`),

  // Провайдеры
  getChatProviders: () =>
    api.get('/chat/providers'),

  // Счетчики
  getUnseenCount: () =>
    api.get('/chat/unseen-count'),
};

React Query интеграция

Система использует React Query для кеширования и синхронизации данных:

// Ключи запросов
export const MULTICHAT_QUERY_KEYS = {
  chats: ['multichat', 'chats'] as const,
  chat: (chatId: number) => ['multichat', 'chats', chatId] as const,
  chatMessages: (chatId: number) => ['multichat', 'chats', chatId, 'messages'] as const,
  chatProviders: ['multichat', 'providers'] as const,
  unseenCount: ['multichat', 'unseen-count'] as const,
} as const;

// Инвалидация кеша
queryClient.invalidateQueries({ queryKey: MULTICHAT_QUERY_KEYS.chats });

Стилизация

Компоненты используют styled-components с поддержкой тем:

const Root = styled.div`
  background-color: var(--graphite-graphite-20);
  border-radius: 8px;

  ${p => p.$mobileView && css`
    width: calc(100vw - var(--sidebar-width));
  `}
`;

Обработка событий

Система поддерживает real-time обновления через WebSocket события:

// Обработчик событий чата
const chatEventHandler = {
  subscribe: (context: MultichatContextValue) => {
    // Подписка на события
  },

  unsubscribe: () => {
    // Отписка от событий
  }
};

Android приложение

Архитектура

Приложение построено на базе MVVM архитектуры с использованием Android Architecture Components:

  • Activities/Fragments - представление данных
  • ViewModels - бизнес-логика и управление состоянием
  • Repositories - абстракция доступа к данным
  • Retrofit - HTTP клиент для API
  • Room - локальная база данных (опционально)
  • WorkManager - фоновая синхронизация

Основные Activity

ChatActivity

Основная активность для отображения чата с полным жизненным циклом управления.

Жизненный цикл:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityChatBinding.inflate(layoutInflater)
    setContentView(binding.root)

    // Инициализация ViewModel
    viewModel.initialize(serverUrl, token, apiKey, chatId)

    // Настройка UI компонентов
    setupViews()
    observeViewModel()
    observeAddressBook()
}

override fun onResume() {
    super.onResume()
    viewModel.setChatActive(true) // Активация чата
}

override fun onPause() {
    super.onPause()
    viewModel.setChatActive(false) // Деактивация чата
}

Основные функции:

  • Отображение списка сообщений с RecyclerView
  • Отправка текстовых сообщений
  • Отправка файлов через FilePicker
  • Ответы на сообщения с визуальным индикатором
  • Автоматическая отметка сообщений как прочитанные при прокрутке
  • Управление прикрепленными файлами

Ключевые компоненты:

  • MessageAdapter - адаптер для RecyclerView с поддержкой разных типов сообщений
  • ChatViewModel - управление состоянием чата и бизнес-логикой
  • NotificationHelper - обработка push-уведомлений
  • BackgroundSyncWorker - фоновая синхронизация

MainActivity

Главная активность с навигацией и списком чатов.

Особенности:

  • BottomNavigationView для переключения разделов
  • SwipeRefreshLayout для обновления списка чатов
  • FloatingActionButton для создания новых чатов
  • Управление жизненным циклом всех чатов

CreateChatActivity

Активность для создания новых чатов с пошаговым мастером.

Этапы создания:

  1. Выбор типа чата (персональный/групповой)
  2. Выбор участников через UserSelectionActivity
  3. Настройка параметров чата
  4. Создание и переход к чату

ViewModels

ChatViewModel

Управляет состоянием конкретного чата и сообщениями.

Состояния:

sealed class ChatState {
    object Loading : ChatState()
    data class Success(val chat: Chat) : ChatState()
    data class Error(val message: String) : ChatState()
}

sealed class MessagesState {
    object Loading : MessagesState()
    data class Success(val messages: List<ChatMessage>) : MessagesState()
    data class Error(val message: String) : MessagesState()
}

sealed class SendMessageState {
    object Loading : SendMessageState()
    object Success : SendMessageState()
    data class Error(val message: String) : SendMessageState()
}

Ключевые методы:

// Загрузка данных чата
fun refreshChat()

// Загрузка сообщений
fun refreshMessages()

// Отправка сообщения
fun sendMessage(text: String, replyToMessage: ChatMessage?, fileIds: List<String>? = null)

// Отправка файла
fun sendFileMessage(uri: Uri, contentResolver: ContentResolver)

// Отметка как прочитанное
fun markMessagesAsRead()

// Управление активностью чата
fun setChatActive(active: Boolean)

MainViewModel

Глобальная ViewModel для управления приложением.

Функции:

  • Управление списком чатов
  • Кеширование данных пользователей (адресная книга)
  • Синхронизация состояния между активностями
  • Управление подключением к серверу

Ключевые методы:

// Получение имени пользователя
fun getUserName(userId: Long): String

// Получение URL аватара
fun getUserAvatarUrl(userId: Long): String?

// Уведомление об обновлении чата
fun notifyChatUpdated(chatId: Long)

// Управление адресной книгой
fun refreshAddressBook()

Репозитории

ChatRepository

Репозиторий для работы с API чатов с поддержкой кеширования.

Основные методы:

suspend fun getChats(): Result<List<Chat>>
suspend fun getChat(chatId: Long): Result<Chat>
suspend fun getMessages(chatId: Long, limit: Int = 50, offset: Long? = null): Result<List<ChatMessage>>
suspend fun sendMessage(chatId: Long, message: SendChatMessageDto): Result<ChatMessage>
suspend fun uploadFile(uri: Uri, contentResolver: ContentResolver): Result<List<UploadedFile>>
suspend fun markAsRead(chatId: Long): Result<Unit>
suspend fun updateMessageStatus(chatId: Long, messageId: Long, status: ChatMessageStatus): Result<Unit>

Обработка ошибок:

sealed class Result<T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Failure<T>(val exception: Throwable) : Result<T>()
}

Модели данных

ChatRequests.kt

DTO для запросов к API:

data class CreatePersonalChatRequest(
    val providerId: Long,
    val companionId: Long
)

data class CreateGroupChatRequest(
    val providerId: Long,
    val title: String,
    val participantIds: List<Long>,
    val entityId: Long? = null
)

data class UpdateGroupChatRequest(
    val title: String? = null,
    val participantIds: List<Long>? = null
)

ChatModels.kt

Основные модели данных приложения:

data class Chat(
    val id: Long,
    val providerId: Long,
    val type: String,
    val title: String?,
    val users: List<ChatUserItem>,
    val lastMessage: ChatMessage?,
    val unseenCount: Int,
    val createdAt: String
)

data class ChatMessage(
    val id: Long,
    val chatId: Long,
    val chatUserId: Long,
    val text: String?,
    val replyToId: Long?,
    val files: List<ChatMessageFile>,
    val reactions: List<ChatMessageReaction>,
    val status: ChatMessageStatus,
    val createdAt: String
)

data class ChatUserItem(
    val id: Long,
    val userId: Long?,
    val name: String?,
    val avatarUrl: String?
)

UI Компоненты

MessageAdapter

Адаптер для отображения сообщений в RecyclerView.

Типы ViewHolder:

  • TextMessageViewHolder - текстовые сообщения
  • FileMessageViewHolder - сообщения с файлами
  • ReplyMessageViewHolder - ответы на сообщения
  • SystemMessageViewHolder - системные сообщения

Особенности:

  • Поддержка разных типов сообщений через getItemViewType()
  • Оптимизация производительности с DiffUtil
  • Обработка кликов и долгого нажатия
  • Анимации для новых сообщений

MessageAdapter.kt

class MessageAdapter(
    private val getUserName: (Long) -> String,
    private val chatUsers: List<ChatUserItem>?,
    private val onScrollToMessage: (Long) -> Unit,
    private val onReplyToMessage: (ChatMessage) -> Unit,
    private val serverUrl: String,
    private val token: String?,
    private val apiKey: String?
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val messages = mutableListOf<ChatMessage>()

    fun submitList(newMessages: List<ChatMessage>) {
        val diffResult = DiffUtil.calculateDiff(MessageDiffCallback(messages, newMessages))
        messages.clear()
        messages.addAll(newMessages)
        diffResult.dispatchUpdatesTo(this)
    }
}

Background Sync

BackgroundSyncWorker

Worker для фоновой синхронизации данных.

Функции:

  • Периодическая проверка новых сообщений
  • Обновление уведомлений
  • Синхронизация аватаров пользователей
  • Обработка оффлайн режима

Конфигурация:

val syncWork = PeriodicWorkRequestBuilder<BackgroundSyncWorker>(
    15, TimeUnit.MINUTES // Синхронизация каждые 15 минут
)
.addConstraints(
    Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()
)
.build()

Уведомления

NotificationHelper

Управление push-уведомлениями и локальными уведомлениями.

Функции:

  • Создание уведомлений о новых сообщениях
  • Группировка уведомлений по чатам
  • Кастомные звуки и вибрация
  • Deep linking в конкретные чаты
fun showMessageNotification(
    chatId: Long,
    message: ChatMessage,
    senderName: String,
    chatTitle: String
) {
    val intent = Intent(context, ChatActivity::class.java).apply {
        putExtra("chat_id", chatId)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }

    val pendingIntent = PendingIntent.getActivity(
        context, chatId.toInt(), intent, PendingIntent.FLAG_IMMUTABLE
    )

    val notification = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentTitle(chatTitle)
        .setContentText("$senderName: ${message.text}")
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)
        .build()

    notificationManager.notify(chatId.toInt(), notification)
}

Работа с файлами

File Handling

Обработка загрузки и отображения файлов.

Поддерживаемые операции:

  • Выбор файлов через Intent.ACTION_GET_CONTENT
  • Загрузка файлов на сервер
  • Отображение превью изображений
  • Скачивание файлов
  • Управление разрешениями на хранение
private fun uploadFileAndSendMessage(message: String) {
    selectedFileUri?.let { uri ->
        lifecycleScope.launch {
            try {
                val result = ChatRepository(apiClient).uploadFile(uri, contentResolver)
                result.fold(
                    onSuccess = { files ->
                        viewModel.sendMessage(message, replyingToMessage, files.map { it.id })
                        clearFileSelection()
                    },
                    onFailure = { error ->
                        Toast.makeText(this@ChatActivity, "Upload failed: ${error.message}", Toast.LENGTH_SHORT).show()
                    }
                )
            } catch (e: Exception) {
                Toast.makeText(this@ChatActivity, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

Темизация и локализация

Поддержка тем:

  • Светлая тема
  • Темная тема
  • Системная тема

Локализация:

  • Русский язык
  • Английский язык
  • Поддержка RTL для арабского и иврита

Производительность

Оптимизации:

  • ViewBinding вместо findViewById
  • DiffUtil для RecyclerView
  • Кеширование изображений с Glide
  • Пейджинг для больших списков
  • Background threading для тяжелых операций

Интеграция с внешними провайдерами

Поддерживаемые провайдеры

  1. Facebook Messenger

    • Требует: page_id, page_access_token, user_access_token
    • Транспорт: messenger
  2. Telegram

    • Интеграция через Telegram API
  3. WhatsApp (Twilio)

    • Требует: account_sid, auth_token, phone_number
    • Транспорт: whatsapp
  4. Whazzup

    • Требует: api_key, channel_id, plain_id
    • Транспорт: whatsapp

Настройка провайдеров

Провайдеры настраиваются через интерфейс интеграций в разделе "Settings > Integrations".

Безопасность

Аутентификация

  • JWT токены для API запросов
  • Проверка прав доступа на уровне аккаунта
  • Валидация принадлежности пользователя к чату

Авторизация

  • Проверка доступа к чатам
  • Ролевая модель для групповых чатов
  • Ограничение действий с сообщениями

Real-time обновления

WebSocket

  • Автоматическое обновление сообщений
  • Статусы прочтения в реальном времени
  • Уведомления о новых сообщениях

Background Sync (Android)

  • BackgroundSyncWorker для синхронизации в фоне
  • Обновление уведомлений
  • Синхронизация аватаров пользователей

Файловые вложения

Поддерживаемые форматы

  • Изображения, документы, видео, аудио
  • Максимальный размер файла настраивается

Загрузка файлов

  • Backend: хранение в файловом хранилище
  • Frontend: drag & drop, выбор файла
  • Android: выбор из галереи/файлового менеджера

Безопасность файлов

  • Валидация MIME типов
  • Сканирование на вирусы (опционально)
  • Ограничение доступа по ссылкам

Поиск и фильтрация

Поиск чатов

  • По имени пользователя (персональные чаты)
  • По содержимому сообщений
  • По связанным сущностям
  • Фильтрация по провайдеру

Полнотекстовый поиск

  • Индексы в базе данных
  • Поиск по тексту сообщений
  • Поддержка регулярных выражений

Мониторинг и логирование

Метрики

  • Количество активных чатов
  • Статистика сообщений
  • Производительность API

Логи

  • Детальное логирование API запросов
  • Ошибки интеграции с провайдерами
  • Аудит действий пользователей

Расширение системы

Добавление нового провайдера

  1. Создать конфигурацию провайдера в базе данных
  2. Реализовать интеграцию в ChatProviderService
  3. Добавить DTO для настроек
  4. Создать миграцию для новой таблицы провайдера
  5. Обновить UI для настройки

Кастомизация UI

  • Темизация компонентов
  • Кастомные аватары
  • Дополнительные поля в сообщениях

Заключение

Система чата предоставляет полнофункциональное решение для коммуникации в CRM, поддерживая множественные каналы связи, real-time обновления и интеграцию с бизнес-логикой системы. Архитектура обеспечивает масштабируемость, безопасность и гибкость для будущих расширений.