Skip to content

Commit 640e329

Browse files
committed
✨ feat: 添加游戏内嵌 svg 地址
1 parent f6f69ad commit 640e329

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed

game/backend/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import sessionStore from 'session-file-store';
77
import { Controller } from "./controller";
88
import cookieParser from 'cookie-parser';
99
import createRoutes from "./routes/api";
10+
import EmbedRouter from "./routes/embed";
1011
import utils from './utils'
1112
import configRouter from "./routes/config";
1213
import { AppDataSource, initDataSource } from "./entities";
@@ -70,6 +71,7 @@ export class Game {
7071
.catch((error) => console.log(error));
7172
}
7273
this.app.use("/api", createRoutes(this, gameName));
74+
this.app.use("/embed", EmbedRouter);
7375
this.controller = new Controller(server);
7476
this.controller?.run();
7577
console.info(`Server running at http://${domain}:${serverPort}/`);

game/backend/src/routes/embed.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { Router, Request, Response } from "express";
2+
import { Controller } from "../controller";
3+
import { login as fishpiLogin, register as fishpiRegister, updateUserInfo } from "../login/fishpi";
4+
import { login as steamLogin } from "../login/steam";
5+
import { bind as steamBind } from "../login/steam";
6+
import { login as githubLogin, bind as githubBind } from "../login/github";
7+
import { login as wechatLogin, bind as wechatBind } from "../login/wechat";
8+
import { Record, RecordRepo, User, UserRepo, AppDataSource, PlayerStats, ManageRepo, UserBindRepo } from "@/entities";
9+
import { getPlayerStats, getPlayerStat, isConfigured } from "@/utils";
10+
import { FindOptionsWhere, Like } from "typeorm";
11+
import GameRouter from "./game";
12+
import Games, { GameRoom } from "@/games";
13+
import { getThirdPartyType, saveUser } from "@/login";
14+
import FishPi from "fishpi";
15+
16+
17+
const router = Router();
18+
19+
router.get('/:username/:game', async (req: Request, res: Response) => {
20+
const { username, game } = req.params;
21+
22+
try {
23+
const stats = await getPlayerStat(username, game);
24+
const gameName = Games[game]?.name || game;
25+
let htmlContent = '';
26+
if (stats) {
27+
const winPercent = stats.total > 0 ? (stats.wins / stats.total * 100) : 0;
28+
const drawPercent = stats.total > 0 ? (stats.draws / stats.total * 100) : 0;
29+
const lossPercent = stats.total > 0 ? (stats.losses / stats.total * 100) : 0;
30+
htmlContent = `
31+
<style>
32+
.card { background-color: #f3f4f6; border-radius: 8px; padding: 16px; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; }
33+
.header { display: flex; justify-content: space-between; align-items: baseline; }
34+
.game-name { font-size: 14px; font-weight: bold; margin-bottom: 4px; }
35+
.score-label { font-size: 12px; opacity: 0.6; }
36+
.stats { display: flex; justify-content: space-between; font-size: 12px; }
37+
.win { color: #16a34a; }
38+
.draw { color: #ca8a04; }
39+
.loss { color: #dc2626; }
40+
.progress-bar { width: 100%; background-color: #e5e7eb; border-radius: 9999px; height: 8px; margin-top: 8px; overflow: hidden; display: flex; }
41+
.progress-win { background-color: #16a34a; height: 100%; }
42+
.progress-draw { background-color: #ca8a04; height: 100%; }
43+
.progress-loss { background-color: #dc2626; height: 100%; }
44+
.score-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px 0; }
45+
.score { font-size: 30px; font-weight: 900; color: #06b6d4; }
46+
.bottom { text-align: right; font-size: 12px; margin-top: 4px; opacity: 0.6; padding-right: 4px; }
47+
</style>
48+
<div class="card">
49+
<header class="header">
50+
<div class="game-name">${gameName}</div>
51+
${stats.score ? '<div class="score-label">最高得分</div>' : ''}
52+
</header>
53+
${!stats.score ? `
54+
<div class="stats">
55+
<span class="win">胜: ${stats.wins}</span>
56+
<span class="draw">平: ${stats.draws}</span>
57+
<span class="loss">负: ${stats.losses}</span>
58+
</div>
59+
<div class="progress-bar">
60+
<div class="progress-win" style="width: ${winPercent}%;"></div>
61+
<div class="progress-draw" style="width: ${drawPercent}%;"></div>
62+
<div class="progress-loss" style="width: ${lossPercent}%;"></div>
63+
</div>
64+
` : `
65+
<div class="score-container">
66+
<div class="score">${stats.score}</div>
67+
</div>
68+
`}
69+
<div class="bottom">游玩次数:${stats.total}</div>
70+
</div>
71+
<div class="bottom">@${username}</div>
72+
`;
73+
} else {
74+
htmlContent = `
75+
<style>
76+
.no-data { background-color: #f3f4f6; border-radius: 8px; padding: 16px; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
77+
.no-data-text { text-align: center; color: #6b7280; }
78+
</style>
79+
<div class="no-data">
80+
<div class="no-data-text">未找到 ${username}${gameName} 中的战绩</div>
81+
</div>
82+
`;
83+
}
84+
const svg = `
85+
<svg width="400" height="168" xmlns="http://www.w3.org/2000/svg">
86+
<foreignObject x="0" y="0" width="400" height="168">
87+
<html xmlns="http://www.w3.org/1999/xhtml">
88+
${htmlContent}
89+
</html>
90+
</foreignObject>
91+
</svg>
92+
`;
93+
res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
94+
res.send(svg);
95+
} catch (error) {
96+
console.error(error);
97+
const errorSvg = `
98+
<svg width="400" height="168" xmlns="http://www.w3.org/2000/svg">
99+
<foreignObject x="0" y="0" width="400" height="168">
100+
<html xmlns="http://www.w3.org/1999/xhtml">
101+
<style>
102+
.error { color: red; text-align: center; padding: 20px; width: 400px; height: 168px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
103+
</style>
104+
<div class="error">服务器错误</div>
105+
</html>
106+
</foreignObject>
107+
</svg>
108+
`;
109+
res.status(500).send(errorSvg);
110+
}
111+
});
112+
113+
export default router;

game/frontend/vite.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export default defineConfig({
2525
target: 'http://127.0.0.1:27015',
2626
changeOrigin: true
2727
},
28+
'/embed': {
29+
target: 'http://127.0.0.1:27015',
30+
changeOrigin: true
31+
},
2832
'/ws': {
2933
target: 'ws://127.0.0.1:27015',
3034
ws: true,

0 commit comments

Comments
 (0)