feat(plugins): catalog images and purchase receipt uploads
Catalog items can store up to three photos; remote tenants attach a receipt on pending requests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
formatIranDate,
|
||||
iranDaysUntil,
|
||||
} from '../common/iran-time.util';
|
||||
import { rewriteLegacyUploadUrl } from '../uploads/uploads.util';
|
||||
import {
|
||||
daysToPlanMonths,
|
||||
inferSettingKeyFromName,
|
||||
@@ -252,6 +253,9 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy {
|
||||
...(dto.description !== undefined && {
|
||||
description: dto.description?.trim() || null,
|
||||
}),
|
||||
...(dto.images !== undefined && {
|
||||
images: this.normalizeImageList(dto.images) as Prisma.InputJsonValue,
|
||||
}),
|
||||
},
|
||||
});
|
||||
for (const price of dto.prices) {
|
||||
@@ -528,6 +532,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy {
|
||||
settingKey: item.settingKey,
|
||||
titleFa: item.titleFa,
|
||||
description: item.description,
|
||||
images: this.normalizeImageList(item.images),
|
||||
prices: enabledPrices.map((p) => ({
|
||||
durationDays: p.durationDays,
|
||||
originalPriceRials: p.originalPriceRials,
|
||||
@@ -655,6 +660,38 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async remoteAttachReceipt(
|
||||
tenantId: string,
|
||||
product: string,
|
||||
requestId: string,
|
||||
receiptUrl: string,
|
||||
) {
|
||||
await this.assertRemoteKhanegi(tenantId, product);
|
||||
const url = rewriteLegacyUploadUrl(receiptUrl);
|
||||
if (!url) throw new BadRequestException('عکس فیش لازم است');
|
||||
const req = await this.prisma.pluginPurchaseRequest.findFirst({
|
||||
where: { id: requestId, tenantId },
|
||||
include: {
|
||||
catalogItem: { select: { titleFa: true, settingKey: true } },
|
||||
tenant: {
|
||||
select: { id: true, nameFa: true, domain: true, slug: true, product: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!req) throw new NotFoundException('درخواست یافت نشد');
|
||||
if (req.status !== 'pending') {
|
||||
throw new BadRequestException('فقط برای درخواست در انتظار میتوان فیش فرستاد');
|
||||
}
|
||||
const updated = await this.prisma.pluginPurchaseRequest.update({
|
||||
where: { id: requestId },
|
||||
data: { receiptUrl: url },
|
||||
});
|
||||
this.notify.notifyAdmins(
|
||||
`فیش واریز افزونه: «${req.catalogItem.titleFa}» برای «${req.tenant.nameFa}» (${req.tenant.domain})`,
|
||||
);
|
||||
return this.toRequestRow({ ...req, receiptUrl: updated.receiptUrl });
|
||||
}
|
||||
|
||||
async runKhanegiExpiry() {
|
||||
try {
|
||||
const plugins = await this.prisma.pluginSubscription.findMany({
|
||||
@@ -759,6 +796,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy {
|
||||
settingKey: item.settingKey,
|
||||
titleFa: item.titleFa,
|
||||
description: item.description,
|
||||
images: this.normalizeImageList(item.images),
|
||||
inStore: item.inStore,
|
||||
prices: PLUGIN_STORE_DURATIONS.map((days) => {
|
||||
const row = priceMap.get(days);
|
||||
@@ -795,11 +833,22 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy {
|
||||
finalPriceRials: row.finalPriceRials,
|
||||
status: row.status,
|
||||
autoCutOnExpiry: row.autoCutOnExpiry,
|
||||
receiptUrl: rewriteLegacyUploadUrl(row.receiptUrl) || null,
|
||||
createdAt: row.createdAt?.toISOString?.() ?? row.createdAt,
|
||||
decidedAt: row.decidedAt?.toISOString?.() ?? row.decidedAt ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeImageList(raw: unknown): string[] {
|
||||
if (!Array.isArray(raw)) return [];
|
||||
const out: string[] = [];
|
||||
for (const item of raw) {
|
||||
const url = rewriteLegacyUploadUrl(item);
|
||||
if (url && !out.includes(url) && out.length < 3) out.push(url);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private readMarks(raw: unknown, expiresAt: Date): Record<string, any> {
|
||||
const iso = expiresAt.toISOString();
|
||||
if (!raw || typeof raw !== 'object') return { _exp: iso };
|
||||
|
||||
Reference in New Issue
Block a user