包含 NestJS 后端、三端前端、Prisma 数据模型、结算引擎测试与 PRD 文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
183 lines
5.8 KiB
TypeScript
183 lines
5.8 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import * as bcrypt from 'bcryptjs';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log('Seeding database...');
|
|
|
|
const superAdminRole = await prisma.role.upsert({
|
|
where: { code: 'SUPER_ADMIN' },
|
|
create: { code: 'SUPER_ADMIN', name: 'Super Admin', description: 'Full access' },
|
|
update: {},
|
|
});
|
|
|
|
const permCodes = [
|
|
'users.create', 'users.view', 'agents.create', 'agents.view',
|
|
'wallet.deposit', 'wallet.withdraw', 'matches.manage', 'settlement.confirm',
|
|
'cashback.confirm', 'content.manage', 'reports.view',
|
|
];
|
|
|
|
for (const code of permCodes) {
|
|
const perm = await prisma.permission.upsert({
|
|
where: { code },
|
|
create: { code, name: code, module: code.split('.')[0] },
|
|
update: {},
|
|
});
|
|
await prisma.rolePermission.upsert({
|
|
where: { roleId_permissionId: { roleId: superAdminRole.id, permissionId: perm.id } },
|
|
create: { roleId: superAdminRole.id, permissionId: perm.id },
|
|
update: {},
|
|
});
|
|
}
|
|
|
|
const hash = await bcrypt.hash('Admin@123', 10);
|
|
const agentHash = await bcrypt.hash('Agent@123', 10);
|
|
const playerHash = await bcrypt.hash('Player@123', 10);
|
|
|
|
await prisma.user.upsert({
|
|
where: { username: 'admin' },
|
|
create: {
|
|
username: 'admin',
|
|
userType: 'ADMIN',
|
|
auth: { create: { passwordHash: hash } },
|
|
adminRole: { create: { roleId: superAdminRole.id } },
|
|
},
|
|
update: {},
|
|
});
|
|
|
|
const agent1 = await prisma.user.upsert({
|
|
where: { username: 'agent1' },
|
|
create: {
|
|
username: 'agent1',
|
|
userType: 'AGENT',
|
|
agentLevel: 1,
|
|
auth: { create: { passwordHash: agentHash } },
|
|
agentProfile: { create: { level: 1, creditLimit: 100000 } },
|
|
},
|
|
update: {},
|
|
});
|
|
|
|
await prisma.agentClosure.upsert({
|
|
where: { ancestorId_descendantId: { ancestorId: agent1.id, descendantId: agent1.id } },
|
|
create: { ancestorId: agent1.id, descendantId: agent1.id, depth: 0 },
|
|
update: {},
|
|
});
|
|
|
|
await prisma.user.upsert({
|
|
where: { username: 'agent2' },
|
|
create: {
|
|
username: 'agent2',
|
|
userType: 'AGENT',
|
|
agentLevel: 2,
|
|
parentId: agent1.id,
|
|
auth: { create: { passwordHash: agentHash } },
|
|
agentProfile: { create: { level: 2, parentAgentId: agent1.id, creditLimit: 30000 } },
|
|
},
|
|
update: {},
|
|
});
|
|
|
|
await prisma.user.upsert({
|
|
where: { username: 'player1' },
|
|
create: {
|
|
username: 'player1',
|
|
userType: 'PLAYER',
|
|
parentId: agent1.id,
|
|
auth: { create: { passwordHash: playerHash } },
|
|
wallet: { create: { availableBalance: 1000 } },
|
|
preferences: { create: { locale: 'zh-CN' } },
|
|
},
|
|
update: {},
|
|
});
|
|
|
|
const messages = [
|
|
{ key: 'nav.home', zh: '首页', ms: 'Laman Utama', en: 'Home' },
|
|
{ key: 'nav.football', zh: '足球', ms: 'Bola Sepak', en: 'Football' },
|
|
{ key: 'bet.place_bet', zh: '确认下注', ms: 'Letak Pertaruhan', en: 'Place Bet' },
|
|
{ key: 'error.insufficient_balance', zh: '余额不足', ms: 'Baki tidak mencukupi', en: 'Insufficient balance' },
|
|
];
|
|
|
|
for (const m of messages) {
|
|
for (const [locale, value] of [['zh-CN', m.zh], ['ms-MY', m.ms], ['en-US', m.en]] as const) {
|
|
await prisma.i18nMessage.upsert({
|
|
where: { msgKey_locale: { msgKey: m.key, locale } },
|
|
create: { msgKey: m.key, locale, value },
|
|
update: { value },
|
|
});
|
|
}
|
|
}
|
|
|
|
const league = await prisma.league.upsert({
|
|
where: { code: 'EPL' },
|
|
create: { code: 'EPL' },
|
|
update: {},
|
|
});
|
|
|
|
await prisma.entityTranslation.upsert({
|
|
where: { entityType_entityId_locale_fieldName: { entityType: 'LEAGUE', entityId: league.id, locale: 'zh-CN', fieldName: 'name' } },
|
|
create: { entityType: 'LEAGUE', entityId: league.id, locale: 'zh-CN', fieldName: 'name', value: '英超' },
|
|
update: {},
|
|
});
|
|
|
|
for (const [code, name] of [['MUN', '曼联'], ['CHE', '切尔西']] as const) {
|
|
const team = await prisma.team.upsert({ where: { code }, create: { code }, update: {} });
|
|
await prisma.entityTranslation.upsert({
|
|
where: { entityType_entityId_locale_fieldName: { entityType: 'TEAM', entityId: team.id, locale: 'zh-CN', fieldName: 'name' } },
|
|
create: { entityType: 'TEAM', entityId: team.id, locale: 'zh-CN', fieldName: 'name', value: name },
|
|
update: { value: name },
|
|
});
|
|
}
|
|
|
|
const mun = await prisma.team.findUnique({ where: { code: 'MUN' } });
|
|
const che = await prisma.team.findUnique({ where: { code: 'CHE' } });
|
|
|
|
if (mun && che) {
|
|
const existing = await prisma.match.findFirst({ where: { homeTeamId: mun.id, awayTeamId: che.id } });
|
|
if (!existing) {
|
|
const match = await prisma.match.create({
|
|
data: {
|
|
leagueId: league.id,
|
|
homeTeamId: mun.id,
|
|
awayTeamId: che.id,
|
|
startTime: new Date(Date.now() + 86400000),
|
|
status: 'PUBLISHED',
|
|
isHot: true,
|
|
publishTime: new Date(),
|
|
},
|
|
});
|
|
await prisma.market.create({
|
|
data: {
|
|
matchId: match.id,
|
|
marketType: 'FT_1X2',
|
|
period: 'FT',
|
|
selections: {
|
|
create: [
|
|
{ selectionCode: 'HOME', selectionName: 'Home', odds: 2.5 },
|
|
{ selectionCode: 'DRAW', selectionName: 'Draw', odds: 3.2 },
|
|
{ selectionCode: 'AWAY', selectionName: 'Away', odds: 2.8 },
|
|
],
|
|
},
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
await prisma.content.create({
|
|
data: {
|
|
contentType: 'BANNER',
|
|
status: 'ACTIVE',
|
|
sortOrder: 1,
|
|
translations: {
|
|
create: [
|
|
{ locale: 'zh-CN', title: '欢迎投注', body: '足球赛事火热进行中' },
|
|
{ locale: 'en-US', title: 'Welcome', body: 'Football matches available' },
|
|
],
|
|
},
|
|
},
|
|
}).catch(() => {});
|
|
|
|
console.log('Seed completed! admin/Admin@123 agent1/Agent@123 player1/Player@123');
|
|
}
|
|
|
|
main().catch(console.error).finally(() => prisma.$disconnect());
|