Files
nademitex_server/src/stock-notify/stock-notify.service.ts
T
2026-09-30 09:53:05 +03:30

157 lines
4.8 KiB
TypeScript

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class StockNotifyService {
constructor(private readonly prisma: PrismaService) {}
async listForCustomer(customerId: string) {
const rows = await this.prisma.stockNotifyRequest.findMany({
where: { customerId },
orderBy: { createdAt: 'desc' },
include: {
product: {
select: {
id: true,
name: true,
code: true,
color: true,
stock: true,
imageUrlsJson: true,
catalog: { select: { code: true } },
},
},
},
});
return rows.map((r) => this.mapCustomer(r));
}
async listAdmin(activeOnly = true) {
const rows = await this.prisma.stockNotifyRequest.findMany({
where: activeOnly ? { active: true } : undefined,
orderBy: { createdAt: 'desc' },
include: {
customer: { select: { firstName: true, lastName: true, phone: true } },
product: { select: { name: true, code: true, color: true, stock: true } },
},
});
return rows.map((r) => this.map(r));
}
async subscribe(customerId: string, productId: string) {
const product = await this.prisma.product.findUnique({ where: { id: productId } });
if (!product) throw new NotFoundException('محصول یافت نشد');
const row = await this.prisma.stockNotifyRequest.upsert({
where: { customerId_productId: { customerId, productId } },
create: { customerId, productId, active: true },
update: { active: true, notifiedAt: null },
include: {
customer: { select: { firstName: true, lastName: true, phone: true } },
product: { select: { name: true, code: true, color: true, stock: true } },
},
});
return this.map(row);
}
async unsubscribe(customerId: string, productId: string) {
await this.prisma.stockNotifyRequest.updateMany({
where: { customerId, productId },
data: { active: false },
});
return { productId, active: false };
}
async markNotified(id: string) {
const existing = await this.prisma.stockNotifyRequest.findUnique({
where: { id },
});
if (!existing) throw new NotFoundException('درخواست یافت نشد');
const row = await this.prisma.stockNotifyRequest.update({
where: { id },
data: { notifiedAt: new Date(), active: false },
include: {
customer: { select: { firstName: true, lastName: true, phone: true } },
product: { select: { name: true, code: true, color: true, stock: true } },
},
});
await this.prisma.customerNotification.create({
data: {
customerId: row.customerId,
event: 'stock_available',
title: 'موجودی محصول',
body: `محصول ${row.product.name} دوباره موجود شد.`,
tone: 'success',
link: `/product?id=${row.productId}`,
},
});
return this.map(row);
}
private productThumb(imageUrlsJson: unknown): string {
if (!Array.isArray(imageUrlsJson)) return '';
const url = imageUrlsJson.find(
(item): item is string => typeof item === 'string' && item.trim().length > 0,
);
return url?.trim() ?? '';
}
private mapCustomer(r: {
id: string;
productId: string;
active: boolean;
createdAt: Date;
notifiedAt: Date | null;
product: {
id: string;
name: string;
code: string;
color: string;
stock: number | null;
imageUrlsJson: unknown;
catalog: { code: string };
};
}) {
return {
id: r.id,
productId: r.productId,
productName: r.product.name,
productCode: r.product.code,
catalogCode: r.product.catalog.code,
color: r.product.color,
stock: r.product.stock,
imageUrl: this.productThumb(r.product.imageUrlsJson),
active: r.active,
createdAt: r.createdAt.toISOString(),
notifiedAt: r.notifiedAt?.toISOString() ?? null,
};
}
private map(r: {
id: string;
customerId: string;
productId: string;
active: boolean;
createdAt: Date;
notifiedAt: Date | null;
customer: { firstName: string; lastName: string; phone: string };
product: { name: string; code: string; color: string; stock: number | null };
}) {
return {
id: r.id,
customerId: r.customerId,
customerName: `${r.customer.firstName} ${r.customer.lastName}`.trim(),
customerPhone: r.customer.phone,
productId: r.productId,
productName: r.product.name,
productCode: r.product.code,
color: r.product.color,
stock: r.product.stock,
active: r.active,
createdAt: r.createdAt.toISOString(),
notifiedAt: r.notifiedAt?.toISOString(),
};
}
}