Files
game_server/docs/前端链接访问示例.md

463 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端链接访问示例
## 概述
当用户访问链接页面时(如 `https://你的域名/ABC12345`),前端需要自动请求后端获取链接的详细信息,并根据状态显示相应的内容。
## 接口说明
### 1. 获取链接状态(主要接口)
**推荐格式(路径参数):**
```
GET /api/link/{code}/status
```
**兼容格式(查询参数,兼容旧版):**
```
GET /api/link/status?code={code}
GET /api/link/status?codeNo={codeNo}
GET /api/link/status?linkId={linkId}
```
> 💡 **推荐使用路径参数格式**,因为复制粘贴时不容易丢失参数,更符合 RESTful 规范。查询参数格式保留用于兼容已生成的旧链接。
**响应示例:**
```json
{
"codeNo": "ABC12345",
"batchId": 123,
"status": "NEW",
"statusDesc": "新建",
"expireAt": "2024-01-15T16:30:00",
"isExpired": false,
"remainingSeconds": 3600,
"quantity": 50,
"times": 10,
"totalPoints": 500,
"region": null,
"machineId": null,
"loginAt": null,
"createdAt": "2024-01-15T12:00:00",
"updatedAt": "2024-01-15T12:00:00"
}
```
### 2. 检查链接是否存在
```
GET /api/link/{codeNo}/exists
```
**响应:** `true``false`
### 3. 检查链接是否有效
```
GET /api/link/{codeNo}/valid
```
**响应:** `true``false`
## 前端实现示例
### React 组件示例
```jsx
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
const LinkPage = () => {
const { codeNo } = useParams();
const [linkStatus, setLinkStatus] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchLinkStatus();
}, [codeNo]);
const fetchLinkStatus = async () => {
try {
setLoading(true);
// 使用路径参数格式,更不容易丢失链接信息
const response = await fetch(`/api/link/${codeNo}/status`);
if (!response.ok) {
if (response.status === 404) {
setError('链接不存在');
} else {
setError('获取链接信息失败');
}
return;
}
const data = await response.json();
setLinkStatus(data);
} catch (err) {
setError('网络错误');
console.error('获取链接状态失败:', err);
} finally {
setLoading(false);
}
};
const formatTime = (seconds) => {
if (seconds <= 0) return '已过期';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}小时${minutes}分钟`;
} else if (minutes > 0) {
return `${minutes}分钟${secs}秒`;
} else {
return `${secs}秒`;
}
};
if (loading) {
return <div className="loading">加载中...</div>;
}
if (error) {
return (
<div className="error">
<h2>链接错误</h2>
<p>{error}</p>
<p>链接编号: {codeNo}</p>
</div>
);
}
if (!linkStatus) {
return <div>未找到链接信息</div>;
}
return (
<div className="link-page">
<div className="link-header">
<h1>游戏任务链接</h1>
<div className="link-code">链接编号: {linkStatus.codeNo}</div>
</div>
<div className="link-status">
<div className="status-item">
<span className="label">状态:</span>
<span className={`value status-${linkStatus.status.toLowerCase()}`}>
{linkStatus.statusDesc}
</span>
</div>
<div className="status-item">
<span className="label">任务信息:</span>
<span className="value">
{linkStatus.times}次副本每次{linkStatus.quantity}
总计{linkStatus.totalPoints}
</span>
</div>
<div className="status-item">
<span className="label">过期时间:</span>
<span className="value">
{linkStatus.expireAt}
</span>
</div>
<div className="status-item">
<span className="label">剩余时间:</span>
<span className={`value ${linkStatus.isExpired ? 'expired' : ''}`}>
{formatTime(linkStatus.remainingSeconds)}
</span>
</div>
</div>
{linkStatus.isExpired && (
<div className="expired-notice">
<p>⚠️ 此链接已过期无法使用</p>
</div>
)}
{!linkStatus.isExpired && linkStatus.status === 'NEW' && (
<div className="action-section">
<h3>开始任务</h3>
<p>点击下方按钮开始执行任务</p>
<button className="start-btn" onClick={() => startTask()}>
开始任务
</button>
</div>
)}
{linkStatus.status === 'USING' && (
<div className="task-progress">
<h3>任务进行中</h3>
<p>请按照提示完成游戏任务</p>
<div className="progress-bar">
<div className="progress-fill" style={{width: '50%'}}></div>
</div>
</div>
)}
{linkStatus.status === 'LOGGED_IN' && (
<div className="task-complete">
<h3>任务完成</h3>
<p>恭喜任务已完成奖励点数已发放</p>
<div className="reward-info">
获得奖励: {linkStatus.totalPoints}
</div>
</div>
)}
<div className="qr-section">
<h3>扫码访问</h3>
<img
src={`/api/link/${codeNo}/qr.png`}
alt="二维码"
className="qr-code"
/>
<p>使用手机扫描二维码访问</p>
</div>
</div>
);
};
const startTask = () => {
// 实现开始任务的逻辑
console.log('开始任务');
};
export default LinkPage;
```
### Vue 组件示例
```vue
<template>
<div class="link-page">
<div v-if="loading" class="loading">加载中...</div>
<div v-else-if="error" class="error">
<h2>链接错误</h2>
<p>{{ error }}</p>
<p>链接编号: {{ codeNo }}</p>
</div>
<div v-else-if="linkStatus" class="link-content">
<div class="link-header">
<h1>游戏任务链接</h1>
<div class="link-code">链接编号: {{ linkStatus.codeNo }}</div>
</div>
<div class="link-status">
<div class="status-item">
<span class="label">状态:</span>
<span :class="['value', `status-${linkStatus.status.toLowerCase()}`]">
{{ linkStatus.statusDesc }}
</span>
</div>
<div class="status-item">
<span class="label">任务信息:</span>
<span class="value">
{{ linkStatus.times }}次副本每次{{ linkStatus.quantity }}
总计{{ linkStatus.totalPoints }}
</span>
</div>
<div class="status-item">
<span class="label">剩余时间:</span>
<span :class="['value', { 'expired': linkStatus.isExpired }]">
{{ formatTime(linkStatus.remainingSeconds) }}
</span>
</div>
</div>
<div v-if="linkStatus.isExpired" class="expired-notice">
<p>⚠️ 此链接已过期无法使用</p>
</div>
<div v-if="!linkStatus.isExpired && linkStatus.status === 'NEW'" class="action-section">
<h3>开始任务</h3>
<button class="start-btn" @click="startTask">开始任务</button>
</div>
<div class="qr-section">
<h3>扫码访问</h3>
<img
:src="`/api/link/${codeNo}/qr.png`"
alt="二维码"
class="qr-code"
/>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'LinkPage',
data() {
return {
codeNo: '',
linkStatus: null,
loading: true,
error: null
};
},
async mounted() {
this.codeNo = this.$route.params.codeNo;
await this.fetchLinkStatus();
},
methods: {
async fetchLinkStatus() {
try {
this.loading = true;
// 使用路径参数格式,更不容易丢失链接信息
const response = await fetch(`/api/link/${this.codeNo}/status`);
if (!response.ok) {
if (response.status === 404) {
this.error = '链接不存在';
} else {
this.error = '获取链接信息失败';
}
return;
}
this.linkStatus = await response.json();
} catch (err) {
this.error = '网络错误';
console.error('获取链接状态失败:', err);
} finally {
this.loading = false;
}
},
formatTime(seconds) {
if (seconds <= 0) return '已过期';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}小时${minutes}分钟`;
} else if (minutes > 0) {
return `${minutes}分钟${secs}秒`;
} else {
return `${secs}秒`;
}
},
startTask() {
// 实现开始任务的逻辑
console.log('开始任务');
}
}
};
</script>
```
## 路由配置
### React Router
```jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import LinkPage from './components/LinkPage';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/:codeNo" element={<LinkPage />} />
{/* 其他路由 */}
</Routes>
</BrowserRouter>
);
}
```
### Vue Router
```javascript
import { createRouter, createWebHistory } from 'vue-router';
import LinkPage from '@/components/LinkPage.vue';
const routes = [
{
path: '/:codeNo',
name: 'LinkPage',
component: LinkPage
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
```
## 使用流程
1. **用户访问链接**`https://你的域名/ABC12345`
2. **前端自动请求**:调用 `/api/link/ABC12345/status` 获取链接信息(使用路径参数,更不容易丢失)
3. **显示相应内容**:根据链接状态显示不同的界面
4. **实时更新**:可以定时刷新状态,显示剩余时间等
## 接口格式说明
系统同时支持两种访问格式,保证新旧链接都能正常使用:
### 方式一:路径参数格式(推荐 ⭐)
```
GET /api/link/{code}/status
```
**示例:**
```javascript
fetch('/api/link/ABC12345/status')
```
**优势:**
- ✅ 复制粘贴时不会丢失参数
- ✅ 符合 RESTful 设计规范
- ✅ URL 结构更清晰
- ✅ 浏览器地址栏直接可见完整路径
### 方式二:查询参数格式(兼容旧版)
```
GET /api/link/status?code={code}
GET /api/link/status?codeNo={codeNo}
GET /api/link/status?linkId={linkId}
```
**示例:**
```javascript
fetch('/api/link/status?code=ABC12345')
fetch('/api/link/status?codeNo=ABC12345')
fetch('/api/link/status?linkId=123')
```
**说明:**
- 保留此格式用于兼容已生成的旧链接
- 支持 `code``codeNo``linkId` 三种参数名
- `linkId``code/codeNo` 至少提供一个即可
### 兼容性保证
- ✅ 两种格式返回完全相同的数据结构
- ✅ 旧链接继续有效,无需修改
- ✅ 新生成的链接推荐使用路径参数格式
## 注意事项
1. **错误处理**:处理链接不存在、已过期等情况
2. **加载状态**:显示加载中的状态,提升用户体验
3. **响应式设计**:确保在不同设备上都能正常显示
4. **缓存策略**:可以适当缓存链接状态,减少请求次数
5. **实时更新**:对于进行中的任务,可以定时刷新状态