first commit
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import mongoose, { Document, Model, Schema, model } from "mongoose";
|
||||
import {
|
||||
CreateMessageParams,
|
||||
FindMessageParams,
|
||||
IMessage,
|
||||
IMessagesAdapter,
|
||||
PaginateMessageParams,
|
||||
PaginateMessageResults,
|
||||
} from "./Messages.Interface";
|
||||
|
||||
export interface IMessageDocument extends Omit<IMessage, "id">, Document {}
|
||||
|
||||
export class MessagesAdapterMongoose implements IMessagesAdapter {
|
||||
schema: Schema;
|
||||
messageList: Model<IMessageDocument>;
|
||||
|
||||
constructor() {
|
||||
this.schema = new Schema({
|
||||
conversationId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: "MessagesConversation",
|
||||
},
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: "User",
|
||||
},
|
||||
message: { type: String, required: true },
|
||||
messageDate: { type: Date, required: true },
|
||||
readed: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
this.messageList = model<IMessageDocument>("Message", this.schema);
|
||||
}
|
||||
|
||||
public async create(data: CreateMessageParams): Promise<IMessage> {
|
||||
return await this.messageList.create({
|
||||
conversationId: data.conversationId,
|
||||
userId: data.sessionUser,
|
||||
message: data.message,
|
||||
messageDate: new Date(),
|
||||
readed: false,
|
||||
});
|
||||
}
|
||||
|
||||
public async delete(id: string): Promise<void> {
|
||||
await this.messageList.findByIdAndDelete(id).exec();
|
||||
}
|
||||
|
||||
public async find(filters: Omit<FindMessageParams, "sessionUser">): Promise<IMessage[]> {
|
||||
return this.messageList.find(filters).exec();
|
||||
}
|
||||
|
||||
public async findOne(
|
||||
filters: Omit<FindMessageParams, "sessionUser">
|
||||
): Promise<IMessage | null> {
|
||||
return this.messageList.findOne(filters).exec();
|
||||
}
|
||||
|
||||
// Método para búsqueda con expresiones regulares y paginación
|
||||
public async paginate(data: PaginateMessageParams): Promise<PaginateMessageResults> {
|
||||
const skip = (data.page - 1) * data.limit;
|
||||
|
||||
// Construye los criterios de búsqueda
|
||||
const searchCriteria: mongoose.FilterQuery<IMessage> = {};
|
||||
|
||||
// Agrega filtro por query usando expresiones regulares para 'name' y 'description'
|
||||
if (data.query) {
|
||||
const regex = new RegExp(data.query, "i");
|
||||
searchCriteria.$or = [{ subject: regex }, { message: regex }];
|
||||
}
|
||||
|
||||
// Filtra por categoryId si está presente
|
||||
if (data.conversationId !== undefined) {
|
||||
searchCriteria.conversationId = data.conversationId;
|
||||
}
|
||||
|
||||
if (data.readed) {
|
||||
searchCriteria.readed = data.readed;
|
||||
}
|
||||
|
||||
// Realiza la consulta con paginación
|
||||
const results = await this.messageList
|
||||
.find(searchCriteria)
|
||||
.sort({ messageDate: -1 })
|
||||
.skip(skip)
|
||||
.limit(data.limit)
|
||||
.exec();
|
||||
|
||||
// Cuenta el total de documentos que coinciden con los criterios de búsqueda
|
||||
const count = await this.messageList.countDocuments(searchCriteria).exec();
|
||||
|
||||
return {
|
||||
data: results,
|
||||
page: data.page,
|
||||
pages: Math.ceil(count / data.limit),
|
||||
};
|
||||
}
|
||||
|
||||
public async markAsRead(data: FindMessageParams): Promise<void> {
|
||||
await this.messageList.updateMany(
|
||||
{ conversationId: data.conversationId },
|
||||
{ $set: { readed: true } }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
CreateMessageConversationParams,
|
||||
IMessageConversation,
|
||||
MESSAGE_CONVERSATION_TYPES,
|
||||
} from "../MessageConversations/MessagesConversations.Interface";
|
||||
|
||||
export type FindMessageParams = {
|
||||
id?: string;
|
||||
conversationId?: string;
|
||||
messageDate?: Date;
|
||||
readed?: boolean;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type PaginateMessageParams = {
|
||||
query?: string;
|
||||
conversationId?: string;
|
||||
dateFrom?: Date;
|
||||
dateTo?: Date;
|
||||
readed?: boolean;
|
||||
page: number;
|
||||
limit: number;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type PaginateMessageResults = {
|
||||
data: IMessage[];
|
||||
page: number;
|
||||
pages: number;
|
||||
};
|
||||
|
||||
export type SendMessageParams = Omit<CreateMessageConversationParams, "type"> & {
|
||||
conversationId?: string;
|
||||
type?: MESSAGE_CONVERSATION_TYPES;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type CreateMessageParams = {
|
||||
conversationId: string;
|
||||
message: string;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type DeleteMessageParams = {
|
||||
id: string;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export interface IMessage {
|
||||
id?: string;
|
||||
conversationId?: string;
|
||||
messageDate?: Date; //Fecha de envío del mensaje
|
||||
readed: boolean; //Indica si el mensaje fue leido por el receptor.
|
||||
message: string; //Contenido del mensaje.
|
||||
userId: string; //Propietario del mensaje.
|
||||
}
|
||||
|
||||
export interface ISendMessageResult {
|
||||
message: IMessage;
|
||||
conversation: IMessageConversation;
|
||||
}
|
||||
|
||||
export interface IMessagesAdapter {
|
||||
create(data: CreateMessageParams): Promise<IMessage>;
|
||||
delete(id: string): Promise<void>;
|
||||
find(filters: FindMessageParams): Promise<IMessage[]>;
|
||||
findOne(filters: FindMessageParams): Promise<IMessage | null>;
|
||||
paginate(filters: PaginateMessageParams): Promise<PaginateMessageResults>;
|
||||
markAsRead(data: FindMessageParams): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IMessageManager {
|
||||
messages: IMessagesAdapter;
|
||||
sendMessage(data: SendMessageParams): Promise<ISendMessageResult>;
|
||||
deleteMessage(data: DeleteMessageParams): Promise<void>;
|
||||
paginateMessages(data: PaginateMessageParams): Promise<PaginateMessageResults>;
|
||||
markAsRead(data: FindMessageParams): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import UsersManager from "../Users/Users";
|
||||
import {
|
||||
DeleteMessageParams,
|
||||
FindMessageParams,
|
||||
IMessageManager,
|
||||
ISendMessageResult,
|
||||
PaginateMessageParams,
|
||||
PaginateMessageResults,
|
||||
SendMessageParams,
|
||||
} from "./Messages.Interface";
|
||||
import { MessagesAdapterMongoose } from "./Messages.Adapter.Mongoose";
|
||||
import {
|
||||
IMessageConversation,
|
||||
MESSAGE_CONVERSATION_TYPES,
|
||||
} from "../MessageConversations/MessagesConversations.Interface";
|
||||
import MessageConversationList from "../MessageConversations/MessagesConversations";
|
||||
import { NotificationsManager } from "../Notifications/Notifications";
|
||||
import EmployeesList from "../Employees/Employee";
|
||||
import CompaniesList from "../Companies/Companies";
|
||||
import { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";
|
||||
|
||||
class MessageManager implements IMessageManager {
|
||||
messages: MessagesAdapterMongoose;
|
||||
|
||||
constructor() {
|
||||
this.messages = new MessagesAdapterMongoose();
|
||||
}
|
||||
|
||||
public async sendMessage(data: SendMessageParams): Promise<ISendMessageResult> {
|
||||
/*
|
||||
* Cuando envio un mensaje tengo dos opciones:
|
||||
* 1. Enviar un mensaje a una conversacion existente
|
||||
* - En este caso debo proporcionar un conversationId.
|
||||
* 2. Crear una nueva conversacion
|
||||
* - En este caso debo proporcionar el tipo de conversacion que se va ha crear
|
||||
* y los datos necesarios para crearla.
|
||||
*/
|
||||
|
||||
if (data.sessionUser) {
|
||||
const userCheck = await UsersManager.users.findOne({ _id: data.sessionUser });
|
||||
if (!userCheck) {
|
||||
throw new Error("Usuario invalido");
|
||||
}
|
||||
}
|
||||
|
||||
let conversation: IMessageConversation | null = null;
|
||||
|
||||
if (data.conversationId) {
|
||||
conversation = await MessageConversationList.conversations.findOne({
|
||||
_id: data.conversationId,
|
||||
});
|
||||
if (!conversation) {
|
||||
throw new Error("Conversacion invalida");
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.conversationId) {
|
||||
if (!data.type) {
|
||||
throw new Error("El tipo de la conversación es requerido");
|
||||
}
|
||||
|
||||
if (data.type === MESSAGE_CONVERSATION_TYPES.USER_TO_USER) {
|
||||
conversation = await MessageConversationList.conversations.findOne({
|
||||
userIdFrom: data.sessionUser,
|
||||
userIdTo: data.userIdTo,
|
||||
});
|
||||
|
||||
if (!conversation) {
|
||||
conversation = await MessageConversationList.createConversation({
|
||||
userIdFrom: data.sessionUser,
|
||||
userIdTo: data.userIdTo,
|
||||
type: MESSAGE_CONVERSATION_TYPES.USER_TO_USER,
|
||||
sessionUser: data.sessionUser,
|
||||
});
|
||||
}
|
||||
} else if (data.type === MESSAGE_CONVERSATION_TYPES.USER_TO_ORG) {
|
||||
conversation = await MessageConversationList.conversations.findOne({
|
||||
userIdFrom: data.sessionUser,
|
||||
companyId: data.companyId,
|
||||
});
|
||||
|
||||
if (!conversation) {
|
||||
conversation = await MessageConversationList.createConversation({
|
||||
userIdFrom: data.sessionUser,
|
||||
companyId: data.companyId,
|
||||
type: MESSAGE_CONVERSATION_TYPES.USER_TO_ORG,
|
||||
sessionUser: data.sessionUser,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!conversation) {
|
||||
throw new Error("No se pudo crear la conversacion");
|
||||
}
|
||||
|
||||
if (!conversation.id) {
|
||||
throw new Error("No se pudo crear la conversacion");
|
||||
}
|
||||
|
||||
//Cuando alguien contesta la conversacion se pasa a user to user.
|
||||
//De esta manera el userTo pasa a ser el que contesto la conversacion (data.sessionUser).
|
||||
if (
|
||||
conversation.companyId &&
|
||||
!conversation.userIdTo &&
|
||||
conversation.userIdFrom != data.sessionUser
|
||||
) {
|
||||
//Convertir la conversation en user to user
|
||||
await MessageConversationList.convertUserToUser({
|
||||
conversationId: conversation.id,
|
||||
sessionUser: data.sessionUser,
|
||||
});
|
||||
}
|
||||
|
||||
const newMessage = await this.messages.create({
|
||||
conversationId: conversation.id,
|
||||
message: data.message,
|
||||
sessionUser: data.sessionUser,
|
||||
});
|
||||
|
||||
const shortMessage =
|
||||
data.message.length > 100 ? data.message.substring(0, 100) + "..." : data.message;
|
||||
|
||||
if (conversation.userIdTo) {
|
||||
const toUser =
|
||||
String(data.sessionUser) === String(conversation.userIdTo)
|
||||
? conversation.userIdFrom
|
||||
: conversation.userIdTo;
|
||||
await NotificationsManager.sendSystemNotification({
|
||||
userId: String(toUser),
|
||||
conversationId: conversation.id,
|
||||
subject: "Nuevo mensaje recibido",
|
||||
message: shortMessage,
|
||||
type: NotificationType.MESSAGE,
|
||||
code: String((newMessage as any)._id)
|
||||
});
|
||||
}
|
||||
|
||||
if (!conversation.userIdTo && conversation.companyId) {
|
||||
const org = await CompaniesList.companies.findOne({
|
||||
_id: conversation.companyId,
|
||||
});
|
||||
|
||||
if (org) {
|
||||
const employees = await EmployeesList.employees.find({
|
||||
companyId: conversation.companyId,
|
||||
});
|
||||
|
||||
for (const employee of employees) {
|
||||
await NotificationsManager.sendSystemNotification({
|
||||
userId: String(employee.userId),
|
||||
conversationId: conversation.id,
|
||||
subject: "Nueva consulta a la organización " + org.name,
|
||||
message: shortMessage,
|
||||
type: NotificationType.MESSAGE,
|
||||
code: String((newMessage as any)._id)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
message: newMessage,
|
||||
conversation: {
|
||||
id: conversation.id,
|
||||
userIdFrom: conversation.userIdFrom,
|
||||
userIdTo: conversation.userIdTo,
|
||||
companyId: conversation.companyId,
|
||||
type: conversation.type,
|
||||
newMessages: conversation.newMessages,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public async deleteMessage(data: DeleteMessageParams): Promise<void> {
|
||||
const checkUser = await UsersManager.users.findOne({ _id: data.sessionUser });
|
||||
if (!checkUser) {
|
||||
throw new Error("Usuario invalido");
|
||||
}
|
||||
|
||||
const messacheCheck = await this.messages.messageList.findOne({
|
||||
_id: data.id,
|
||||
});
|
||||
|
||||
if (!messacheCheck) {
|
||||
throw new Error("Mensaje invalido");
|
||||
}
|
||||
|
||||
if (String(messacheCheck.userId) !== data.sessionUser) {
|
||||
throw new Error("No tienes permiso para eliminar este mensaje");
|
||||
}
|
||||
|
||||
await this.messages.delete(data.id);
|
||||
}
|
||||
|
||||
public async paginateMessages(filters: PaginateMessageParams): Promise<PaginateMessageResults> {
|
||||
return await this.messages.paginate(filters);
|
||||
}
|
||||
|
||||
public async markAsRead(data: FindMessageParams): Promise<void> {
|
||||
const checkUser = await UsersManager.users.findOne({ _id: data.sessionUser });
|
||||
if (!checkUser) {
|
||||
throw new Error("Usuario invalido");
|
||||
}
|
||||
this.messages.markAsRead(data);
|
||||
}
|
||||
}
|
||||
|
||||
const MessagesList = new MessageManager();
|
||||
|
||||
export default MessagesList;
|
||||
Reference in New Issue
Block a user