234 lines
16 KiB
TypeScript
234 lines
16 KiB
TypeScript
import { PrismaClient } from "../lib/generated/prisma";
|
||
import bcrypt from "bcryptjs";
|
||
|
||
// DEFAULT_RULES رو مستقیم اینجا تعریف میکنیم تا از @/ alias استفاده نکنیم
|
||
const DEFAULT_RULES = {
|
||
GK: { GOAL: 10, ASSIST: 3, YELLOW_CARD: -1, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 6, PENALTY_SAVED: 5, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
|
||
DEF: { GOAL: 8, ASSIST: 3, YELLOW_CARD: -1, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 4, PENALTY_SAVED: 0, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
|
||
MID: { GOAL: 5, ASSIST: 3, YELLOW_CARD: -2, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 1, PENALTY_SAVED: 0, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
|
||
FWD: { GOAL: 4, ASSIST: 3, YELLOW_CARD: -1, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 0, PENALTY_SAVED: 0, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
|
||
} as const;
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
// ترکیب پیشفرض تیمهای ملی معروف (بر اساس آخرین اطلاعات)
|
||
const COUNTRY_FORMATIONS: Record<string, string> = {
|
||
BRA: "4-3-3", FRA: "4-3-3", ARG: "4-3-3", ENG: "4-3-3",
|
||
ESP: "4-3-3", GER: "4-2-3-1", POR: "4-3-3", NED: "4-3-3",
|
||
BEL: "4-3-3", CRO: "4-3-3", MAR: "4-3-3", IRN: "4-5-1",
|
||
URU: "4-3-3", SEN: "4-3-3", KOR: "4-3-3", JPN: "4-3-3",
|
||
MEX: "4-3-3", USA: "4-3-3", CAN: "4-3-3", AUS: "4-3-3",
|
||
POL: "4-3-3", DEN: "4-3-3", SUI: "4-2-3-1", SRB: "3-4-3",
|
||
WAL: "5-3-2", TUN: "4-3-3", CMR: "4-3-3", GHA: "4-2-3-1",
|
||
ECU: "4-3-3", QAT: "5-3-2", KSA: "4-3-3", CRC: "5-4-1",
|
||
};
|
||
|
||
const COUNTRIES = [
|
||
{ name: "برزیل", code: "BRA", flag: "🇧🇷", group: "G" },
|
||
{ name: "سربیا", code: "SRB", flag: "🇷🇸", group: "G" },
|
||
{ name: "سوئیس", code: "SUI", flag: "🇨🇭", group: "G" },
|
||
{ name: "کامرون", code: "CMR", flag: "🇨🇲", group: "G" },
|
||
{ name: "فرانسه", code: "FRA", flag: "🇫🇷", group: "D" },
|
||
{ name: "استرالیا", code: "AUS", flag: "🇦🇺", group: "D" },
|
||
{ name: "دانمارک", code: "DEN", flag: "🇩🇰", group: "D" },
|
||
{ name: "تونس", code: "TUN", flag: "🇹🇳", group: "D" },
|
||
{ name: "آرژانتین", code: "ARG", flag: "🇦🇷", group: "C" },
|
||
{ name: "عربستان", code: "KSA", flag: "🇸🇦", group: "C" },
|
||
{ name: "مکزیک", code: "MEX", flag: "🇲🇽", group: "C" },
|
||
{ name: "لهستان", code: "POL", flag: "🇵🇱", group: "C" },
|
||
{ name: "ایران", code: "IRN", flag: "🇮🇷", group: "B" },
|
||
{ name: "انگلیس", code: "ENG", flag: "🏴", group: "B" },
|
||
{ name: "آمریکا", code: "USA", flag: "🇺🇸", group: "B" },
|
||
{ name: "ولز", code: "WAL", flag: "🏴", group: "B" },
|
||
{ name: "آلمان", code: "GER", flag: "🇩🇪", group: "E" },
|
||
{ name: "ژاپن", code: "JPN", flag: "🇯🇵", group: "E" },
|
||
{ name: "اسپانیا", code: "ESP", flag: "🇪🇸", group: "E" },
|
||
{ name: "کاستاریکا", code: "CRC", flag: "🇨🇷", group: "E" },
|
||
{ name: "بلژیک", code: "BEL", flag: "🇧🇪", group: "F" },
|
||
{ name: "کانادا", code: "CAN", flag: "🇨🇦", group: "F" },
|
||
{ name: "مراکش", code: "MAR", flag: "🇲🇦", group: "F" },
|
||
{ name: "کرواسی", code: "CRO", flag: "🇭🇷", group: "F" },
|
||
{ name: "پرتغال", code: "POR", flag: "🇵🇹", group: "H" },
|
||
{ name: "غنا", code: "GHA", flag: "🇬🇭", group: "H" },
|
||
{ name: "اروگوئه", code: "URU", flag: "🇺🇾", group: "H" },
|
||
{ name: "کره جنوبی", code: "KOR", flag: "🇰🇷", group: "H" },
|
||
{ name: "هلند", code: "NED", flag: "🇳🇱", group: "A" },
|
||
{ name: "سنگال", code: "SEN", flag: "🇸🇳", group: "A" },
|
||
{ name: "اکوادور", code: "ECU", flag: "🇪🇨", group: "A" },
|
||
{ name: "قطر", code: "QAT", flag: "🇶🇦", group: "A" },
|
||
];
|
||
|
||
const PLAYERS_DATA = [
|
||
{ name: "آلیسون بکر", pos: "GK", code: "BRA", price: 6.0, pts: 42 },
|
||
{ name: "تیاگو سیلوا", pos: "DEF", code: "BRA", price: 6.5, pts: 38 },
|
||
{ name: "مارکینیوس", pos: "DEF", code: "BRA", price: 6.0, pts: 35 },
|
||
{ name: "کاسمیرو", pos: "MID", code: "BRA", price: 8.0, pts: 52 },
|
||
{ name: "نیمار", pos: "FWD", code: "BRA", price: 11.5, pts: 68 },
|
||
{ name: "وینیسیوس جونیور", pos: "FWD", code: "BRA", price: 10.5, pts: 72 },
|
||
{ name: "ریچارلیسون", pos: "FWD", code: "BRA", price: 8.5, pts: 55 },
|
||
{ name: "هوگو لوریس", pos: "GK", code: "FRA", price: 6.0, pts: 40 },
|
||
{ name: "رافائل واران", pos: "DEF", code: "FRA", price: 6.5, pts: 36 },
|
||
{ name: "کیلیان امباپه", pos: "FWD", code: "FRA", price: 12.5, pts: 88 },
|
||
{ name: "آنتوان گریزمان", pos: "MID", code: "FRA", price: 9.5, pts: 62 },
|
||
{ name: "اولیویه ژیرو", pos: "FWD", code: "FRA", price: 7.5, pts: 48 },
|
||
{ name: "اوسمان دمبله", pos: "FWD", code: "FRA", price: 8.0, pts: 44 },
|
||
{ name: "امیلیانو مارتینز", pos: "GK", code: "ARG", price: 6.5, pts: 55 },
|
||
{ name: "لیونل مسی", pos: "FWD", code: "ARG", price: 12.5, pts: 92 },
|
||
{ name: "خولیان آلوارز", pos: "FWD", code: "ARG", price: 8.5, pts: 60 },
|
||
{ name: "رودریگو دپاول", pos: "MID", code: "ARG", price: 7.5, pts: 45 },
|
||
{ name: "نیکولاس اوتامندی", pos: "DEF", code: "ARG", price: 5.5, pts: 32 },
|
||
{ name: "جوردن پیکفورد", pos: "GK", code: "ENG", price: 5.5, pts: 38 },
|
||
{ name: "جود بلینگهام", pos: "MID", code: "ENG", price: 10.5, pts: 75 },
|
||
{ name: "هری کین", pos: "FWD", code: "ENG", price: 11.0, pts: 70 },
|
||
{ name: "بوکایو ساکا", pos: "MID", code: "ENG", price: 8.5, pts: 58 },
|
||
{ name: "فیل فودن", pos: "MID", code: "ENG", price: 9.0, pts: 62 },
|
||
{ name: "جان استونز", pos: "DEF", code: "ENG", price: 6.0, pts: 34 },
|
||
{ name: "اونای سیمون", pos: "GK", code: "ESP", price: 5.5, pts: 36 },
|
||
{ name: "پدری", pos: "MID", code: "ESP", price: 9.0, pts: 60 },
|
||
{ name: "گاوی", pos: "MID", code: "ESP", price: 8.5, pts: 55 },
|
||
{ name: "آلوارو موراتا", pos: "FWD", code: "ESP", price: 7.5, pts: 46 },
|
||
{ name: "دنی اولمو", pos: "MID", code: "ESP", price: 7.0, pts: 42 },
|
||
{ name: "مانوئل نویر", pos: "GK", code: "GER", price: 6.0, pts: 38 },
|
||
{ name: "توماس مولر", pos: "MID", code: "GER", price: 8.0, pts: 50 },
|
||
{ name: "کای هاورتز", pos: "MID", code: "GER", price: 8.5, pts: 52 },
|
||
{ name: "یامله موسیالا", pos: "MID", code: "GER", price: 9.0, pts: 65 },
|
||
{ name: "آنتونیو رودیگر", pos: "DEF", code: "GER", price: 5.5, pts: 30 },
|
||
{ name: "دیوگو کوستا", pos: "GK", code: "POR", price: 5.5, pts: 35 },
|
||
{ name: "کریستیانو رونالدو", pos: "FWD", code: "POR", price: 11.0, pts: 65 },
|
||
{ name: "برونو فرناندز", pos: "MID", code: "POR", price: 10.0, pts: 70 },
|
||
{ name: "رافائل لئائو", pos: "FWD", code: "POR", price: 8.5, pts: 55 },
|
||
{ name: "روبن دیاز", pos: "DEF", code: "POR", price: 6.0, pts: 36 },
|
||
{ name: "علیرضا بیرانوند", pos: "GK", code: "IRN", price: 5.0, pts: 28 },
|
||
{ name: "مهدی طارمی", pos: "FWD", code: "IRN", price: 8.0, pts: 50 },
|
||
{ name: "سردار آزمون", pos: "FWD", code: "IRN", price: 7.5, pts: 44 },
|
||
{ name: "علی کریمی", pos: "MID", code: "IRN", price: 6.0, pts: 32 },
|
||
{ name: "رامین رضاییان", pos: "DEF", code: "IRN", price: 5.0, pts: 22 },
|
||
{ name: "یاسین بونو", pos: "GK", code: "MAR", price: 6.5, pts: 58 },
|
||
{ name: "اشرف حکیمی", pos: "DEF", code: "MAR", price: 8.0, pts: 62 },
|
||
{ name: "حکیم زیاش", pos: "MID", code: "MAR", price: 7.5, pts: 48 },
|
||
{ name: "یوسف النصیری", pos: "FWD", code: "MAR", price: 7.0, pts: 45 },
|
||
{ name: "دومینیک لیواکوویچ", pos: "GK", code: "CRO", price: 6.0, pts: 50 },
|
||
{ name: "لوکا مودریچ", pos: "MID", code: "CRO", price: 9.5, pts: 68 },
|
||
{ name: "ایوان پریشیچ", pos: "MID", code: "CRO", price: 7.5, pts: 48 },
|
||
{ name: "آندره کراماریچ", pos: "FWD", code: "CRO", price: 7.0, pts: 44 },
|
||
{ name: "آندریس نوپرت", pos: "GK", code: "NED", price: 5.5, pts: 36 },
|
||
{ name: "ویرخیل فان دایک", pos: "DEF", code: "NED", price: 7.0, pts: 48 },
|
||
{ name: "دنزل دامفریس", pos: "DEF", code: "NED", price: 7.5, pts: 52 },
|
||
{ name: "کودی گاکپو", pos: "FWD", code: "NED", price: 8.0, pts: 58 },
|
||
{ name: "تیبو کورتوا", pos: "GK", code: "BEL", price: 6.5, pts: 44 },
|
||
{ name: "کوین دبروینه", pos: "MID", code: "BEL", price: 11.0, pts: 72 },
|
||
{ name: "رومله لوکاکو", pos: "FWD", code: "BEL", price: 9.5, pts: 55 },
|
||
];
|
||
|
||
async function main() {
|
||
console.log("🌱 Seeding...");
|
||
|
||
// ─── ادمین و کاربران ─────────────────────────────────
|
||
const adminPwd = await bcrypt.hash("admin123", 10);
|
||
await prisma.user.upsert({ where: { email: "admin@worldcup.com" }, update: {},
|
||
create: { email: "admin@worldcup.com", name: "ادمین", password: adminPwd, role: "ADMIN" } });
|
||
|
||
const userPwd = await bcrypt.hash("user123", 10);
|
||
const users = [];
|
||
for (const u of [{ email: "ali@test.com", name: "علی احمدی" }, { email: "sara@test.com", name: "سارا رضایی" }, { email: "reza@test.com", name: "رضا محمدی" }]) {
|
||
const user = await prisma.user.upsert({ where: { email: u.email }, update: {}, create: { ...u, password: userPwd } });
|
||
users.push(user);
|
||
}
|
||
|
||
// ─── گروهها ──────────────────────────────────────────
|
||
const groupMap: Record<string, string> = {};
|
||
for (const name of ["A","B","C","D","E","F","G","H"]) {
|
||
const g = await prisma.group.upsert({ where: { name }, update: {}, create: { name } });
|
||
groupMap[name] = g.id;
|
||
}
|
||
|
||
// ─── تیمهای ملی ─────────────────────────────────────
|
||
const countryMap: Record<string, string> = {};
|
||
for (const c of COUNTRIES) {
|
||
const country = await prisma.country.upsert({ where: { code: c.code }, update: { flagUrl: c.flag, defaultFormation: COUNTRY_FORMATIONS[c.code] ?? "4-3-3" },
|
||
create: { name: c.name, code: c.code, flagUrl: c.flag, groupId: groupMap[c.group], defaultFormation: COUNTRY_FORMATIONS[c.code] ?? "4-3-3" } });
|
||
countryMap[c.code] = country.id;
|
||
}
|
||
|
||
// ─── بازیکنان ─────────────────────────────────────────
|
||
const playersToCreate = PLAYERS_DATA
|
||
.filter(p => countryMap[p.code])
|
||
.map(p => ({ name: p.name, position: p.pos as any, countryId: countryMap[p.code], price: p.price, totalPoints: p.pts }));
|
||
await prisma.player.createMany({ data: playersToCreate, skipDuplicates: true });
|
||
|
||
// ─── قوانین امتیازدهی پیشفرض ────────────────────────
|
||
const positions = ["GK", "DEF", "MID", "FWD"] as const;
|
||
for (const pos of positions) {
|
||
const rules = DEFAULT_RULES[pos];
|
||
for (const [eventType, points] of Object.entries(rules)) {
|
||
await prisma.scoringRule.upsert({
|
||
where: { position_eventType: { position: pos, eventType: eventType as any } },
|
||
update: { points: points as number },
|
||
create: { position: pos, eventType: eventType as any, points: points as number },
|
||
});
|
||
}
|
||
}
|
||
|
||
// ─── دورها ────────────────────────────────────────────
|
||
const round1 = await prisma.round.upsert({ where: { number: 1 }, update: {},
|
||
create: { number: 1, name: "دور اول - مرحله گروهی", isActive: true, deadline: new Date("2026-06-15T10:00:00Z") } });
|
||
const round2 = await prisma.round.upsert({ where: { number: 2 }, update: {},
|
||
create: { number: 2, name: "دور دوم - مرحله گروهی", deadline: new Date("2026-06-22T10:00:00Z") } });
|
||
const round3 = await prisma.round.upsert({ where: { number: 3 }, update: {},
|
||
create: { number: 3, name: "دور سوم - مرحله گروهی", deadline: new Date("2026-06-29T10:00:00Z") } });
|
||
const round4 = await prisma.round.upsert({ where: { number: 4 }, update: {},
|
||
create: { number: 4, name: "دور چهارم - یکهشتم نهایی", deadline: new Date("2026-07-05T10:00:00Z") } });
|
||
|
||
// ─── بازیها ──────────────────────────────────────────
|
||
const matchesData = [
|
||
// دور اول - بازی اول هر تیم
|
||
{ home: "QAT", away: "ECU", hS: 0, aS: 2, st: "FINISHED", date: "2026-06-20T16:00:00Z", rId: round1.id },
|
||
{ home: "ENG", away: "IRN", hS: 6, aS: 2, st: "FINISHED", date: "2026-06-21T13:00:00Z", rId: round1.id },
|
||
{ home: "ARG", away: "KSA", hS: 1, aS: 2, st: "FINISHED", date: "2026-06-22T10:00:00Z", rId: round1.id },
|
||
{ home: "FRA", away: "AUS", hS: 4, aS: 1, st: "FINISHED", date: "2026-06-22T19:00:00Z", rId: round1.id },
|
||
{ home: "GER", away: "JPN", hS: 1, aS: 2, st: "FINISHED", date: "2026-06-23T13:00:00Z", rId: round1.id },
|
||
{ home: "ESP", away: "CRC", hS: 7, aS: 0, st: "FINISHED", date: "2026-06-23T19:00:00Z", rId: round1.id },
|
||
{ home: "BEL", away: "CAN", hS: 1, aS: 0, st: "FINISHED", date: "2026-06-23T16:00:00Z", rId: round1.id },
|
||
{ home: "BRA", away: "SRB", hS: 2, aS: 0, st: "FINISHED", date: "2026-06-24T19:00:00Z", rId: round1.id },
|
||
{ home: "POR", away: "GHA", hS: 3, aS: 2, st: "FINISHED", date: "2026-06-24T16:00:00Z", rId: round1.id },
|
||
{ home: "MAR", away: "CRO", hS: 0, aS: 0, st: "FINISHED", date: "2026-06-23T10:00:00Z", rId: round1.id },
|
||
{ home: "NED", away: "SEN", hS: 2, aS: 0, st: "FINISHED", date: "2026-06-21T16:00:00Z", rId: round1.id },
|
||
// دور دوم - بازی دوم هر تیم
|
||
{ home: "IRN", away: "WAL", hS: 2, aS: 0, st: "FINISHED", date: "2026-06-25T13:00:00Z", rId: round2.id },
|
||
{ home: "FRA", away: "DEN", hS: 2, aS: 1, st: "FINISHED", date: "2026-06-26T19:00:00Z", rId: round2.id },
|
||
{ home: "ARG", away: "MEX", hS: 2, aS: 0, st: "FINISHED", date: "2026-06-26T22:00:00Z", rId: round2.id },
|
||
{ home: "BRA", away: "SUI", hS: 1, aS: 0, st: "FINISHED", date: "2026-06-28T19:00:00Z", rId: round2.id },
|
||
{ home: "ENG", away: "USA", hS: 0, aS: 0, st: "FINISHED", date: "2026-06-25T19:00:00Z", rId: round2.id },
|
||
// دور سوم - بازی سوم گروهی
|
||
{ home: "BRA", away: "CMR", hS: 1, aS: 0, st: "SCHEDULED", date: "2026-07-02T19:00:00Z", rId: round3.id },
|
||
{ home: "FRA", away: "TUN", hS: null, aS: null, st: "SCHEDULED", date: "2026-07-01T16:00:00Z", rId: round3.id },
|
||
// دور چهارم - یکهشتم
|
||
{ home: "NED", away: "ARG", hS: 2, aS: 2, st: "FINISHED", date: "2026-12-09T19:00:00Z", rId: round4.id, stage: "ROUND_OF_16" },
|
||
{ home: "FRA", away: "ENG", hS: 2, aS: 1, st: "FINISHED", date: "2026-12-10T19:00:00Z", rId: round4.id, stage: "ROUND_OF_16" },
|
||
{ home: "ARG", away: "FRA", hS: 3, aS: 3, st: "FINISHED", date: "2026-12-18T15:00:00Z", rId: round4.id, stage: "FINAL" },
|
||
];
|
||
|
||
const matchesToCreate = matchesData
|
||
.filter(m => countryMap[m.home] && countryMap[m.away])
|
||
.map(m => ({
|
||
homeTeamId: countryMap[m.home], awayTeamId: countryMap[m.away],
|
||
homeScore: m.hS ?? null, awayScore: m.aS ?? null,
|
||
status: m.st as any, stage: (m.stage ?? "GROUP") as any,
|
||
matchDate: new Date(m.date), roundId: m.rId,
|
||
}));
|
||
await prisma.match.createMany({ data: matchesToCreate, skipDuplicates: true });
|
||
|
||
// ─── پکیجها ──────────────────────────────────────────
|
||
for (const pkg of [
|
||
{ id: "pkg-silver", name: "پکیج نقرهای", budgetBonus: 10, price: 50000, description: "۱۰ میلیون به بودجه اضافه کن" },
|
||
{ id: "pkg-gold", name: "پکیج طلایی", budgetBonus: 20, price: 90000, description: "۲۰ میلیون به بودجه اضافه کن" },
|
||
{ id: "pkg-diamond", name: "پکیج الماس", budgetBonus: 30, price: 120000, description: "۳۰ میلیون به بودجه اضافه کن" },
|
||
]) {
|
||
await prisma.package.upsert({ where: { id: pkg.id }, update: {}, create: pkg });
|
||
}
|
||
|
||
console.log("✅ Seed done! admin@worldcup.com / admin123 | ali@test.com / user123");
|
||
}
|
||
|
||
main().catch(console.error).finally(() => prisma.$disconnect());
|