feat(admin,api,player): settlement stats, team crests, MS fields and list bet summary

This commit is contained in:
2026-06-04 17:30:48 +08:00
parent cc737e2924
commit 9fcee31a9a
27 changed files with 2296 additions and 427 deletions

View File

@@ -1,4 +1,6 @@
/** 内置国家队(世界杯 48 强 + 常用队),供管理端选择国旗与自动填充队名 */
import { NATIONAL_TEAM_CREST, NATIONAL_TEAM_MS } from './nationalTeamCrests';
/** 内置国家队(世界杯 48 强 + 常用队),供管理端选择国旗/队徽与自动填充队名 */
export type BuiltinCountry = {
code: string;
nameZh: string;
@@ -6,6 +8,8 @@ export type BuiltinCountry = {
iso: string;
};
export type CountryLogoKind = 'flag' | 'crest' | 'custom';
export const BUILTIN_COUNTRIES: BuiltinCountry[] = [
{ code: 'FRA', nameZh: '法国', nameEn: 'France', iso: 'fr' },
{ code: 'ESP', nameZh: '西班牙', nameEn: 'Spain', iso: 'es' },
@@ -20,7 +24,7 @@ export const BUILTIN_COUNTRIES: BuiltinCountry[] = [
{ code: 'COL', nameZh: '哥伦比亚', nameEn: 'Colombia', iso: 'co' },
{ code: 'JPN', nameZh: '日本', nameEn: 'Japan', iso: 'jp' },
{ code: 'URU', nameZh: '乌拉圭', nameEn: 'Uruguay', iso: 'uy' },
{ code: 'USA', nameZh: '美国', nameEn: 'USA', iso: 'us' },
{ code: 'USA', nameZh: '美国', nameEn: 'United States', iso: 'us' },
{ code: 'MAR', nameZh: '摩洛哥', nameEn: 'Morocco', iso: 'ma' },
{ code: 'CRO', nameZh: '克罗地亚', nameEn: 'Croatia', iso: 'hr' },
{ code: 'MEX', nameZh: '墨西哥', nameEn: 'Mexico', iso: 'mx' },
@@ -34,20 +38,20 @@ export const BUILTIN_COUNTRIES: BuiltinCountry[] = [
{ code: 'IRN', nameZh: '伊朗', nameEn: 'Iran', iso: 'ir' },
{ code: 'GHA', nameZh: '加纳', nameEn: 'Ghana', iso: 'gh' },
{ code: 'ALG', nameZh: '阿尔及利亚', nameEn: 'Algeria', iso: 'dz' },
{ code: 'BIH', nameZh: '波黑', nameEn: 'Bosnia', iso: 'ba' },
{ code: 'BIH', nameZh: '波黑', nameEn: 'Bosnia & Herzegovina', iso: 'ba' },
{ code: 'EGY', nameZh: '埃及', nameEn: 'Egypt', iso: 'eg' },
{ code: 'TUN', nameZh: '突尼斯', nameEn: 'Tunisia', iso: 'tn' },
{ code: 'CAN', nameZh: '加拿大', nameEn: 'Canada', iso: 'ca' },
{ code: 'PAN', nameZh: '巴拿马', nameEn: 'Panama', iso: 'pa' },
{ code: 'AUS', nameZh: '澳大利亚', nameEn: 'Australia', iso: 'au' },
{ code: 'CZE', nameZh: '捷克', nameEn: 'Czech Republic', iso: 'cz' },
{ code: 'CZE', nameZh: '捷克', nameEn: 'Czechia', iso: 'cz' },
{ code: 'KSA', nameZh: '沙特阿拉伯', nameEn: 'Saudi Arabia', iso: 'sa' },
{ code: 'NZL', nameZh: '新西兰', nameEn: 'New Zealand', iso: 'nz' },
{ code: 'COD', nameZh: '刚果(金)', nameEn: 'DR Congo', iso: 'cd' },
{ code: 'COD', nameZh: '刚果(金)', nameEn: 'DR Congo', iso: 'cd' },
{ code: 'UZB', nameZh: '乌兹别克斯坦', nameEn: 'Uzbekistan', iso: 'uz' },
{ code: 'IRQ', nameZh: '伊拉克', nameEn: 'Iraq', iso: 'iq' },
{ code: 'RSA', nameZh: '南非', nameEn: 'South Africa', iso: 'za' },
{ code: 'CIV', nameZh: '科特迪瓦', nameEn: 'Ivory Coast', iso: 'ci' },
{ code: 'CIV', nameZh: '象牙海岸', nameEn: "Côte d'Ivoire", iso: 'ci' },
{ code: 'JOR', nameZh: '约旦', nameEn: 'Jordan', iso: 'jo' },
{ code: 'PAR', nameZh: '巴拉圭', nameEn: 'Paraguay', iso: 'py' },
{ code: 'HAI', nameZh: '海地', nameEn: 'Haiti', iso: 'ht' },
@@ -97,6 +101,49 @@ export function countryFlagUrl(country: BuiltinCountry | string): string {
return `https://flagcdn.com/w40/${c.iso}.png`;
}
export function countryCrestUrl(country: BuiltinCountry | string): string {
const c = typeof country === 'string' ? getBuiltinCountry(country) : country;
if (!c) return '';
return NATIONAL_TEAM_CREST[c.code] ?? '';
}
export function hasCountryCrest(country: BuiltinCountry | string): boolean {
return !!countryCrestUrl(country);
}
export function countryLogoUrl(
country: BuiltinCountry | string,
kind: 'flag' | 'crest',
): string {
if (kind === 'crest') {
const crest = countryCrestUrl(country);
if (crest) return crest;
}
return countryFlagUrl(country);
}
export function detectCountryLogoKind(
logoUrl?: string | null,
teamCode?: string | null,
): CountryLogoKind {
const url = (logoUrl ?? '').trim();
if (!url) return 'flag';
const code = (teamCode ?? '').trim().toUpperCase();
const fromCode = code ? getBuiltinCountry(code) : undefined;
if (fromCode) {
if (url === countryCrestUrl(fromCode)) return 'crest';
if (url.includes('flagcdn.com')) return 'flag';
}
for (const c of BUILTIN_COUNTRIES) {
const crest = countryCrestUrl(c);
if (crest && url === crest) return 'crest';
}
if (url.includes('flagcdn.com')) return 'flag';
if (url.includes('website-files.com') && url.includes('footylogos')) return 'crest';
if (url.includes('footballlogos-org')) return 'crest';
return 'custom';
}
/** 按后台当前语言显示国家名(下拉只显示一种语言) */
export function countryDisplayName(c: BuiltinCountry, locale: string): string {
if (locale === 'en-US') return c.nameEn;
@@ -108,53 +155,8 @@ export function countryOptionLabel(c: BuiltinCountry, locale: string): string {
return `${countryDisplayName(c, locale)} (${c.code})`;
}
/** 常用国家队马来语名(无则用英文,避免与中文混排) */
const COUNTRY_MS: Partial<Record<string, string>> = {
CAN: 'Kanada',
USA: 'Amerika Syarikat',
MEX: 'Mexico',
BRA: 'Brazil',
ARG: 'Argentina',
ENG: 'England',
FRA: 'Perancis',
GER: 'Jerman',
ESP: 'Sepanyol',
POR: 'Portugal',
NED: 'Belanda',
BEL: 'Belgium',
CRO: 'Croatia',
SUI: 'Switzerland',
POL: 'Poland',
SWE: 'Sweden',
NOR: 'Norway',
DEN: 'Denmark',
JPN: 'Jepun',
KOR: 'Korea Selatan',
AUS: 'Australia',
RSA: 'Afrika Selatan',
MAR: 'Maghribi',
SEN: 'Senegal',
GHA: 'Ghana',
EGY: 'Mesir',
TUN: 'Tunisia',
ALG: 'Algeria',
KSA: 'Arab Saudi',
QAT: 'Qatar',
IRN: 'Iran',
IRQ: 'Iraq',
CHN: 'China',
THA: 'Thailand',
VIE: 'Vietnam',
IDN: 'Indonesia',
MAS: 'Malaysia',
BIH: 'Bosnia',
SCO: 'Scotland',
WAL: 'Wales',
NZL: 'New Zealand',
};
function countryNameMs(c: BuiltinCountry): string {
return COUNTRY_MS[c.code] ?? c.nameEn;
return NATIONAL_TEAM_MS[c.code] ?? c.nameEn;
}
export function searchBuiltinCountries(keyword: string, locale = 'zh-CN'): BuiltinCountry[] {
@@ -179,7 +181,12 @@ export function resolveCountryCode(
const fromCode = getBuiltinCountry(teamCode);
if (fromCode) return fromCode.code;
if (logoUrl) {
const m = logoUrl.match(/flagcdn\.com\/w\d+\/([a-z0-9-]+)\.png/i);
const trimmed = logoUrl.trim();
const byCrest = BUILTIN_COUNTRIES.find(
(c) => countryCrestUrl(c) && countryCrestUrl(c) === trimmed,
);
if (byCrest) return byCrest.code;
const m = trimmed.match(/flagcdn\.com\/w\d+\/([a-z0-9-]+)\.png/i);
if (m) {
const iso = m[1].toLowerCase();
const hit = BUILTIN_COUNTRIES.find((c) => c.iso === iso);

View File

@@ -0,0 +1,103 @@
/** 世界杯 48 强国家队徽footylogos CDN */
export const NATIONAL_TEAM_CREST: Record<string, string> = {
ARG: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a1048bb2071e9deee459c12_6a104445459be40714bd9774_argentina-national-team-footylogos.png',
AUS: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a10e35263d68e6f439e_69fa8abfc42edca0f730781a_australia-national-team-footylogos.png',
AUT: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68f9fc172630205d3271b9f1_austria-national-team-footballlogos-org.svg',
BEL: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a0fe2c051dd81874aac_68f9fc44d95277458c187c4f_belgium-national-team-footballlogos-org.png',
BIH: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68fd1a0bb96a229b28eff9ca_bosnia-and-herzegovina-footballlogos-org.svg',
BRA: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a07dd70012891100817_68f9fc74eac3dc471d42c1f1_brazil-national-team-footballlogos-org.png',
CPV: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a04f8e4dbd2d7560e3f_68fd1a7a7cceddc21192dd2a_cabo-verde-footballlogos-org.png',
CAN: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a09ff1d0f7cf9fc6892_68f9fcae7c3a768d2112f7cc_canada-national-team-footballlogos-org.png',
COL: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68f9fd84ab47946a360b482d_colombia-national-team-footballlogos-org.svg',
CRO: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68f9fe732030ba1891c2c1e7_croatia-national-team-footballlogos-org.svg',
CUW: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a0c450ecf35a44ec35a_690b58788ae9e26e532abfdf_curacao-national-team-footballlogos-org.png',
CZE: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a124f9b12d3899cdf35_68f9fefa92aa92766cfafa43_czechia-national-team-footballlogos-org.png',
COD: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68fd1b5eda26ecde0ae4f1eb_dr-congo-footballlogos-org.svg',
ECU: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a03e8cfc8f978c35242_68f9ffb9e5f5a02e2ddd6c89_ecuador-national-team-footballlogos-org.png',
EGY: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39fd094c95a818967d17_68f9ffff9f0a363d783fca07_egypt-national-team-footballlogos-org.png',
ENG: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f4f81c2d9713bb1958_68fa004f9bad274585f92fd4_england-national-team-footballlogos-org.png',
FRA: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39fa921e692c7885a3a5_68fa00af9b52a99bf3ce88b8_france-national-team-footballlogos-org.png',
GER: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a134ff2b16d2ff2c420_68fa00ee54de0cacbdff9b16_germany-national-team-footballlogos-org.png',
GHA: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a176f4197f7299d29c76a61_6a121b9d1a9da04ec878bb21_ghana-national-team-footylogos.png',
HAI: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0f4c6c910c48d9bace182a_692869cd3f30b984d69b7f75_haiti-national-team-footylogos.png',
IRN: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f9be2380ec23ca8496_68fa01dc9c4cd3255b45967d_iran-national-team-footballlogos-org.png',
IRQ: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a08c18986f46d9cbb5a_68fd1ce7c48c6f7c6b9f2d9f_iraq-footballlogos-org.png',
CIV: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a0a63e7e11f6ecf95f4_68f9fe0f5e31af81b7ceb973_cote-d-ivoire-national-team-footballlogos-org.png',
JPN: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a179bc2e28bf73ecd09_68fa02b609c96c8ed3c2cdf6_japan-national-team-footballlogos-org.png',
JOR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39fac9c45208a31ce544_68fd1d515ce3de4f3d2e23b0_jordan-footballlogos-org.png',
MEX: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a067e47ced126acdd02_68fa02ee37f495f860481404_mexico-national-team-footballlogos-org.png',
MAR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f3ff709486e36ba903_68fa032284d7ebc8adde9b0e_morocco-national-team-footballlogos-org.png',
NED: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a15786b121c0b7b60cc_68f9ff890403a59958f26579_netherlands-dutch-national-team-footballlogos-org.png',
NZL: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39ff967f6d274aeb4e8b_68fa67ce6d8bf137ee675702_new-zealand-national-team-footballlogos-org.png',
NOR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68fa03b401a24ac6badeaa5c_norway-national-team-footballlogos-org.svg',
PAN: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68fa03e45d0722bd9f321589_panama-national-team-footballlogos-org.svg',
PAR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a15c8ff2f7f7f02602c_68fa042c2750779cccf807b4_paraguay-national-team-footballlogos-org.png',
POR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a0095b66650d7655aba_68fa6b30ffde0dbd282357ab_portugal-national-team-footballlogos-org.png',
QAT: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/68fd1f8e1cc7aebc6fcfff34_qatar-national-team-footballlogos-org.svg',
KSA: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f8751bc84c4be421cf_68fd1fc871c2e4977d6345f9_saudi-arabia-national-team-footballlogos-org.png',
SEN: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a052fcac044e973b7e1_68fa07554408d744a38f143f_senegal-national-team-footballlogos-org.png',
KOR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a18adae7bad8cb6e423_68fa0855071867dd20c531f3_south-korea-national-team-footballlogos-org.png',
RSA: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f717d2d9b8868d47d7_68fd1ff449702964723a7890_south-africa-national-team-footballlogos-org.png',
ESP: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a1aa6db0dbe288b67f3_68fa08931b5e1697f8930e74_spain-national-team-footballlogos-org.png',
SWE: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f2940ca8a6d33ef41f_68fa08ce61f58c56e735629c_sweden-national-team-footballlogos-org.png',
SUI: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a0d052b7e092df739cf_68fa0904f28db91037150be9_swiss-national-team-footballlogos-org.png',
TUN: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39fc9b5ae9ed29ad6646_68fa093b578f3b5329f80833_tunisia-national-team-footballlogos-org.png',
TUR: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a199844733813bb5b41_68fa09923df7f921ed9a86ef_turkey-national-team-footballlogos-org.png',
UZB: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39fc12418491a747e862_68fd208e9bfd4ed3a9b30b6f_uzbekistan-national-team-footballlogos-org.png',
URU: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a06d17821a2db9019ea_68fa0a1577c6612bb1154a07_uruguay-national-team-footballlogos-org.png',
USA: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39f54269adc0c7a000c3_68fa0a65e8dbfe6ba33cb5e6_usa-national-team-footballlogos-org.png',
ALG: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b39fec61707555515b17e_68f9faa3abd65d5f6209b2cd_algeria-national-team-footballlogos-org.png',
SCO: 'https://cdn.prod.website-files.com/68f550992570ca0322737dc2/6a0b3a0e29b605309fb7f47b_68fa07134a7663c774887b1a_scotland-national-team-footballlogos-org.png',
};
/** 48 强马来语队名(与队徽表一致) */
export const NATIONAL_TEAM_MS: Record<string, string> = {
ARG: 'Argentina',
AUS: 'Australia',
AUT: 'Austria',
BEL: 'Belgium',
BIH: 'Bosnia dan Herzegovina',
BRA: 'Brazil',
CPV: 'Tanjung Verde',
CAN: 'Kanada',
COL: 'Colombia',
CRO: 'Croatia',
CUW: 'Curaçao',
CZE: 'Republik Czech',
COD: 'Republik Demokratik Congo',
ECU: 'Ecuador',
EGY: 'Mesir',
ENG: 'England',
FRA: 'Perancis',
GER: 'Jerman',
GHA: 'Ghana',
HAI: 'Haiti',
IRN: 'Iran',
IRQ: 'Iraq',
CIV: "Côte d'Ivoire",
JPN: 'Jepun',
JOR: 'Jordan',
MEX: 'Mexico',
MAR: 'Maghribi',
NED: 'Belanda',
NZL: 'New Zealand',
NOR: 'Norway',
PAN: 'Panama',
PAR: 'Paraguay',
POR: 'Portugal',
QAT: 'Qatar',
KSA: 'Arab Saudi',
SEN: 'Senegal',
KOR: 'Korea Selatan',
RSA: 'Afrika Selatan',
ESP: 'Sepanyol',
SWE: 'Sweden',
SUI: 'Switzerland',
TUN: 'Tunisia',
TUR: 'Turki',
UZB: 'Uzbekistan',
URU: 'Uruguay',
USA: 'Amerika Syarikat',
ALG: 'Algeria',
SCO: 'Scotland',
};