feat: 世界杯48强夺冠盘、管理端调赔与项目文档

- 固定48强基准数据、同步种子与后台世界杯夺冠页

- 补全 user_preferences 迁移文件;新增启动指南与默认数据说明

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 16:19:36 +08:00
parent 3b739982a1
commit 95abbcb470
17 changed files with 1157 additions and 92 deletions

View File

@@ -0,0 +1,3 @@
-- AlterTable: user_preferences 增加手机、邮箱(玩家资料)
ALTER TABLE "user_preferences" ADD COLUMN IF NOT EXISTS "phone" VARCHAR(32);
ALTER TABLE "user_preferences" ADD COLUMN IF NOT EXISTS "email" VARCHAR(128);

View File

@@ -1,5 +1,6 @@
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcryptjs';
import { syncWc2026OutrightMarket } from '../src/domains/catalog/wc2026-outright.sync';
const prisma = new PrismaClient();
@@ -359,81 +360,11 @@ async function seedOutrightDemo() {
const wc = await prisma.league.findUnique({ where: { code: 'WC2026' } });
if (!wc) return;
const placeholder = await upsertTeam('OUT', { 'zh-CN': '冠军盘', 'en-US': 'Outright' });
const outrightOdds: Array<[string, Record<string, string>, number]> = [
['FRA', { 'zh-CN': '法国', 'en-US': 'France' }, 4.95],
['ESP', { 'zh-CN': '西班牙', 'en-US': 'Spain' }, 4.95],
['ENG', { 'zh-CN': '英格兰', 'en-US': 'England' }, 6.3],
['BRA', { 'zh-CN': '巴西', 'en-US': 'Brazil' }, 8.55],
['ARG', { 'zh-CN': '阿根廷', 'en-US': 'Argentina' }, 8.55],
['POR', { 'zh-CN': '葡萄牙', 'en-US': 'Portugal' }, 9.0],
['GER', { 'zh-CN': '德国', 'en-US': 'Germany' }, 15.3],
['NED', { 'zh-CN': '荷兰', 'en-US': 'Netherlands' }, 18.9],
['NOR', { 'zh-CN': '挪威', 'en-US': 'Norway' }, 32.4],
['BEL', { 'zh-CN': '比利时', 'en-US': 'Belgium' }, 35.1],
['COL', { 'zh-CN': '哥伦比亚', 'en-US': 'Colombia' }, 45.9],
['JPN', { 'zh-CN': '日本', 'en-US': 'Japan' }, 45.9],
['URU', { 'zh-CN': '乌拉圭', 'en-US': 'Uruguay' }, 63.9],
['USA', { 'zh-CN': '美国', 'en-US': 'USA' }, 63.9],
['MAR', { 'zh-CN': '摩洛哥', 'en-US': 'Morocco' }, 63.9],
['CRO', { 'zh-CN': '克罗地亚', 'en-US': 'Croatia' }, 81.0],
['MEX', { 'zh-CN': '墨西哥', 'en-US': 'Mexico' }, 85.0],
['SUI', { 'zh-CN': '瑞士', 'en-US': 'Switzerland' }, 90.0],
['TUR', { 'zh-CN': '土耳其', 'en-US': 'Turkey' }, 95.0],
['SEN', { 'zh-CN': '塞内加尔', 'en-US': 'Senegal' }, 100.0],
];
for (const [code, names] of outrightOdds) {
await upsertTeam(code, names);
}
let match = await prisma.match.findFirst({
where: { leagueId: wc.id, isOutright: true },
const { matchId, marketId } = await syncWc2026OutrightMarket(prisma, {
forceCanonical: true,
});
if (!match) {
match = await prisma.match.create({
data: {
leagueId: wc.id,
homeTeamId: placeholder.id,
awayTeamId: placeholder.id,
isOutright: true,
startTime: new Date('2027-07-01T00:00:00Z'),
status: 'PUBLISHED',
publishTime: new Date(),
isHot: true,
displayOrder: 0,
},
});
}
const marketExists = await prisma.market.findFirst({
where: { matchId: match.id, marketType: 'OUTRIGHT_WINNER' },
});
if (!marketExists) {
await prisma.market.create({
data: {
matchId: match.id,
marketType: 'OUTRIGHT_WINNER',
period: 'OUTRIGHT',
allowSingle: true,
allowParlay: false,
sortOrder: 1,
status: 'OPEN',
selections: {
create: outrightOdds.map(([code, names, odds], i) => ({
selectionCode: code,
selectionName: names['zh-CN'],
odds,
sortOrder: i,
status: 'OPEN',
})),
},
},
});
}
console.log(' Outright demo: World Cup winner market');
const count = await prisma.marketSelection.count({ where: { marketId } });
console.log(` WC2026 outright: match ${matchId}, ${count} selections`);
}
async function seedPlayerDemo() {