feat: باشگاه امتیازات کارکنان (موتور امتیاز، جدول رتبه، فروشگاه پاداش، نشان‌ها، پنل مدیریت و گزارش ROI)

- موتور رویدادمحور با شرط، محدودیت، تأیید مدیر و idempotency
- اتصال به لید، مشتری، معامله، فعالیت، روتین، پیش‌فاکتور، فاکتور و تیکت
- صفحه /club و /club/admin، اعلان زنده امتیاز، آیتم منو
- انتقال فرم‌ساز سرنخ به ابزارها

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-10 21:28:49 +03:30
co-authored by Cursor
parent b87ebb22e0
commit 60d797f184
51 changed files with 11298 additions and 806 deletions
@@ -39,6 +39,7 @@ import {
} from './routine-mapper';
import { DEFAULT_REMIND_MINUTES } from './routine-generator.service';
import { RoutineStatsService, effectiveTotal } from './routine-stats.service';
import { GamificationService } from '../gamification/gamification.service';
import {
addDays,
dayKeyToDate,
@@ -55,6 +56,7 @@ export class RoutineTasksService {
private readonly prisma: PrismaService,
private readonly notifications: NotificationsService,
private readonly stats: RoutineStatsService,
private readonly game: GamificationService,
) {}
scope(actor: Actor): Promise<RoutineScope> {
@@ -395,6 +397,19 @@ export class RoutineTasksService {
{ status: 'completed', completedAt: new Date(), resultNote: note?.trim() || row.resultNote },
[{ type: 'status_changed', note: note?.trim() || undefined }],
);
this.game.emit({
organizationId: actor.organizationId,
userId: row.assigneeId,
event: row.routineId ? 'ROUTINE_COMPLETED' : 'TASK_COMPLETED',
entityType: 'routine_task',
entityId: row.id,
entityTitle: row.title,
facts: {
onTime: row.status !== 'overdue' && (!row.dueAt || Date.now() <= row.dueAt.getTime()),
isMandatory: row.isMandatory,
priority: row.priority,
},
});
return toTaskDto(updated);
}