Marketplace móvil para negocios locales mexicanos. Estructura inicial: - apps/backend (NestJS API) - apps/frontend (React Web) - apps/mobile (Expo/React Native) - apps/mcp-server (Claude MCP Server) - apps/whatsapp-service (WhatsApp Business API) - database/ (PostgreSQL DDL) - docs/ (Documentación) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
146 lines
5.8 KiB
JavaScript
146 lines
5.8 KiB
JavaScript
"use strict";
|
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
};
|
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
};
|
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.MessagingService = void 0;
|
|
const common_1 = require("@nestjs/common");
|
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
const typeorm_2 = require("typeorm");
|
|
const conversation_entity_1 = require("./entities/conversation.entity");
|
|
const message_entity_1 = require("./entities/message.entity");
|
|
const notification_entity_1 = require("./entities/notification.entity");
|
|
let MessagingService = class MessagingService {
|
|
constructor(conversationRepo, messageRepo, notificationRepo) {
|
|
this.conversationRepo = conversationRepo;
|
|
this.messageRepo = messageRepo;
|
|
this.notificationRepo = notificationRepo;
|
|
}
|
|
async getConversations(tenantId) {
|
|
return this.conversationRepo.find({
|
|
where: { tenantId },
|
|
order: { lastMessageAt: 'DESC' },
|
|
});
|
|
}
|
|
async getConversation(tenantId, id) {
|
|
const conversation = await this.conversationRepo.findOne({
|
|
where: { id, tenantId },
|
|
relations: ['messages'],
|
|
});
|
|
if (!conversation) {
|
|
throw new common_1.NotFoundException('Conversación no encontrada');
|
|
}
|
|
return conversation;
|
|
}
|
|
async findOrCreateConversation(tenantId, phoneNumber, contactName) {
|
|
let conversation = await this.conversationRepo.findOne({
|
|
where: { tenantId, phoneNumber },
|
|
});
|
|
if (!conversation) {
|
|
conversation = this.conversationRepo.create({
|
|
tenantId,
|
|
phoneNumber,
|
|
contactName,
|
|
conversationType: 'general',
|
|
status: 'active',
|
|
});
|
|
await this.conversationRepo.save(conversation);
|
|
}
|
|
return conversation;
|
|
}
|
|
async getMessages(conversationId, limit = 50) {
|
|
return this.messageRepo.find({
|
|
where: { conversationId },
|
|
order: { createdAt: 'DESC' },
|
|
take: limit,
|
|
});
|
|
}
|
|
async addMessage(conversationId, direction, content, type = message_entity_1.MessageType.TEXT, metadata) {
|
|
const conversation = await this.conversationRepo.findOne({
|
|
where: { id: conversationId },
|
|
});
|
|
if (!conversation) {
|
|
throw new common_1.NotFoundException('Conversación no encontrada');
|
|
}
|
|
const message = this.messageRepo.create({
|
|
conversationId,
|
|
direction,
|
|
messageType: type,
|
|
content,
|
|
waMessageId: metadata?.waMessageId,
|
|
mediaUrl: metadata?.mediaUrl,
|
|
mediaMimeType: metadata?.mediaMimeType,
|
|
});
|
|
await this.messageRepo.save(message);
|
|
conversation.lastMessageAt = new Date();
|
|
conversation.lastMessagePreview = content?.substring(0, 100);
|
|
if (direction === message_entity_1.MessageDirection.INBOUND) {
|
|
conversation.unreadCount += 1;
|
|
}
|
|
await this.conversationRepo.save(conversation);
|
|
return message;
|
|
}
|
|
async markAsRead(tenantId, conversationId) {
|
|
const conversation = await this.getConversation(tenantId, conversationId);
|
|
conversation.unreadCount = 0;
|
|
return this.conversationRepo.save(conversation);
|
|
}
|
|
async getNotifications(tenantId, userId, unreadOnly = false) {
|
|
const where = { tenantId };
|
|
if (userId) {
|
|
where.userId = userId;
|
|
}
|
|
if (unreadOnly) {
|
|
where.readAt = null;
|
|
}
|
|
return this.notificationRepo.find({
|
|
where,
|
|
order: { createdAt: 'DESC' },
|
|
take: 50,
|
|
});
|
|
}
|
|
async createNotification(tenantId, data) {
|
|
const notification = this.notificationRepo.create({
|
|
tenantId,
|
|
...data,
|
|
});
|
|
return this.notificationRepo.save(notification);
|
|
}
|
|
async markNotificationRead(notificationId) {
|
|
const notification = await this.notificationRepo.findOne({
|
|
where: { id: notificationId },
|
|
});
|
|
if (!notification) {
|
|
throw new common_1.NotFoundException('Notificación no encontrada');
|
|
}
|
|
notification.readAt = new Date();
|
|
return this.notificationRepo.save(notification);
|
|
}
|
|
async getUnreadCount(tenantId, userId) {
|
|
const where = { tenantId, readAt: null };
|
|
if (userId) {
|
|
where.userId = userId;
|
|
}
|
|
return this.notificationRepo.count({ where });
|
|
}
|
|
};
|
|
exports.MessagingService = MessagingService;
|
|
exports.MessagingService = MessagingService = __decorate([
|
|
(0, common_1.Injectable)(),
|
|
__param(0, (0, typeorm_1.InjectRepository)(conversation_entity_1.Conversation)),
|
|
__param(1, (0, typeorm_1.InjectRepository)(message_entity_1.Message)),
|
|
__param(2, (0, typeorm_1.InjectRepository)(notification_entity_1.Notification)),
|
|
__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
typeorm_2.Repository,
|
|
typeorm_2.Repository])
|
|
], MessagingService);
|
|
//# sourceMappingURL=messaging.service.js.map
|