mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 19:51:17 +00:00
65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
import { randomInt } from "crypto";
|
|
import { Prisma } from "@/src/generated/prisma/client";
|
|
import { prisma } from "@/src/lib/prisma";
|
|
import { berlinDayEnd } from "@/src/modules/courses/lib/course-days";
|
|
import type { Reward, TriggerKind } from "./node-config";
|
|
|
|
// Ohne 0/O und 1/I, damit niemand den Code beim Abtippen verwechselt.
|
|
const ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
|
|
const PREFIX: Record<TriggerKind, string> = {
|
|
birthday: "GEBURTSTAG",
|
|
milestone: "DANKE",
|
|
winback: "COMEBACK",
|
|
};
|
|
|
|
export function generateCodeString(kind: TriggerKind): string {
|
|
let suffix = "";
|
|
for (let i = 0; i < 6; i++) suffix += ALPHABET[randomInt(ALPHABET.length)];
|
|
return `${PREFIX[kind]}-${suffix}`;
|
|
}
|
|
|
|
/**
|
|
* Legt den persönlichen Rabattcode für eine Marketing-Mail an: einmal
|
|
* einlösbar, nur für dieses Konto, gültig bis einschließlich `validUntilDay`
|
|
* (Berliner Tag). Er erscheint nicht auf der Rabattcode-Seite (origin
|
|
* "marketing"), sondern im Verlauf des Marketings.
|
|
*/
|
|
export async function createPersonalDiscountCode(params: {
|
|
businessId: string;
|
|
userId: string;
|
|
triggerKind: TriggerKind;
|
|
reward: Reward;
|
|
validUntilDay: string;
|
|
}): Promise<{ id: string; code: string; expiresAt: Date }> {
|
|
const expiresAt = berlinDayEnd(params.validUntilDay);
|
|
|
|
// Kollisionen sind bei 32^6 Möglichkeiten pro Präfix selten - ein paar
|
|
// Versuche reichen.
|
|
for (let attempt = 1; ; attempt++) {
|
|
try {
|
|
return await prisma.discountCode
|
|
.create({
|
|
data: {
|
|
businessId: params.businessId,
|
|
code: generateCodeString(params.triggerKind),
|
|
type: params.reward.type,
|
|
value: params.reward.value,
|
|
appliesTo: params.reward.appliesTo,
|
|
scope: "all_courses",
|
|
courseIds: [],
|
|
maxRedemptions: 1,
|
|
expiresAt,
|
|
restrictedToUserId: params.userId,
|
|
origin: "marketing",
|
|
},
|
|
select: { id: true, code: true, expiresAt: true },
|
|
})
|
|
.then((code) => ({ ...code, expiresAt: code.expiresAt ?? expiresAt }));
|
|
} catch (error) {
|
|
const isCollision =
|
|
error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002";
|
|
if (!isCollision || attempt >= 5) throw error;
|
|
}
|
|
}
|
|
}
|