mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-11 07:06:28 +08:00
146 lines
4.1 KiB
TypeScript
146 lines
4.1 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { Cron, CronExpression } from '@nestjs/schedule';
|
|
import { PrismaClient } from '@prisma/client';
|
|
|
|
import { EventBus, JobQueue, OnJob } from '../../base';
|
|
import {
|
|
SubscriptionPlan,
|
|
SubscriptionRecurring,
|
|
SubscriptionVariant,
|
|
} from './types';
|
|
|
|
declare global {
|
|
interface Jobs {
|
|
'nightly.cleanExpiredOnetimeSubscriptions': {};
|
|
'nightly.notifyAboutToExpireWorkspaceSubscriptions': {};
|
|
}
|
|
}
|
|
|
|
@Injectable()
|
|
export class SubscriptionCronJobs {
|
|
constructor(
|
|
private readonly db: PrismaClient,
|
|
private readonly event: EventBus,
|
|
private readonly queue: JobQueue
|
|
) {}
|
|
|
|
private getDateRange(after: number, base: number | Date = Date.now()) {
|
|
const start = new Date(base);
|
|
start.setDate(start.getDate() + after);
|
|
start.setHours(0, 0, 0, 0);
|
|
|
|
const end = new Date(start);
|
|
end.setHours(23, 59, 59, 999);
|
|
|
|
return { start, end };
|
|
}
|
|
|
|
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
|
|
async nightlyJob() {
|
|
await this.queue.add(
|
|
'nightly.cleanExpiredOnetimeSubscriptions',
|
|
{},
|
|
{
|
|
jobId: 'nightly-payment-clean-expired-onetime-subscriptions',
|
|
}
|
|
);
|
|
|
|
// FIXME(@forehalo): the strategy is totally wrong, for monthly plan. redesign required
|
|
// await this.queue.add(
|
|
// 'nightly.notifyAboutToExpireWorkspaceSubscriptions',
|
|
// {},
|
|
// {
|
|
// jobId: 'nightly-payment-notify-about-to-expire-workspace-subscriptions',
|
|
// }
|
|
// );
|
|
}
|
|
|
|
@OnJob('nightly.notifyAboutToExpireWorkspaceSubscriptions')
|
|
async notifyAboutToExpireWorkspaceSubscriptions() {
|
|
const { start: after30DayStart, end: after30DayEnd } =
|
|
this.getDateRange(30);
|
|
const { start: todayStart, end: todayEnd } = this.getDateRange(0);
|
|
const { start: before150DaysStart, end: before150DaysEnd } =
|
|
this.getDateRange(-150);
|
|
const { start: before180DaysStart, end: before180DaysEnd } =
|
|
this.getDateRange(-180);
|
|
|
|
const subscriptions = await this.db.subscription.findMany({
|
|
where: {
|
|
plan: SubscriptionPlan.Team,
|
|
OR: [
|
|
{
|
|
// subscription will cancel after 30 days
|
|
status: 'active',
|
|
canceledAt: { not: null },
|
|
end: { gte: after30DayStart, lte: after30DayEnd },
|
|
},
|
|
{
|
|
// subscription will cancel today
|
|
status: 'active',
|
|
canceledAt: { not: null },
|
|
end: { gte: todayStart, lte: todayEnd },
|
|
},
|
|
{
|
|
// subscription has been canceled for 150 days
|
|
// workspace becomes delete after 180 days
|
|
status: 'canceled',
|
|
canceledAt: { gte: before150DaysStart, lte: before150DaysEnd },
|
|
},
|
|
{
|
|
// subscription has been canceled for 180 days
|
|
// workspace becomes delete after 180 days
|
|
status: 'canceled',
|
|
canceledAt: { gte: before180DaysStart, lte: before180DaysEnd },
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
for (const subscription of subscriptions) {
|
|
const end = subscription.end;
|
|
if (!end) {
|
|
// should not reach here
|
|
continue;
|
|
}
|
|
|
|
if (!subscription.nextBillAt) {
|
|
this.event.emit('workspace.subscription.notify', {
|
|
workspaceId: subscription.targetId,
|
|
expirationDate: end,
|
|
deletionDate: this.getDateRange(180, end).end,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
@OnJob('nightly.cleanExpiredOnetimeSubscriptions')
|
|
async cleanExpiredOnetimeSubscriptions() {
|
|
const subscriptions = await this.db.subscription.findMany({
|
|
where: {
|
|
variant: SubscriptionVariant.Onetime,
|
|
end: {
|
|
lte: new Date(),
|
|
},
|
|
},
|
|
});
|
|
|
|
for (const subscription of subscriptions) {
|
|
await this.db.subscription.delete({
|
|
where: {
|
|
targetId_plan: {
|
|
targetId: subscription.targetId,
|
|
plan: subscription.plan,
|
|
},
|
|
},
|
|
});
|
|
|
|
this.event.emit('user.subscription.canceled', {
|
|
userId: subscription.targetId,
|
|
plan: subscription.plan as SubscriptionPlan,
|
|
recurring: subscription.variant as SubscriptionRecurring,
|
|
});
|
|
}
|
|
}
|
|
}
|