新增系统配置表及默认配置,更新链接生成请求DTO以支持链接数量参数,重构链接生成服务逻辑,添加链接状态查询和有效性检查接口,优化日志记录。

This commit is contained in:
zyh
2025-08-26 10:33:26 +08:00
parent 7317866f98
commit 599ec0a36b
73 changed files with 1829 additions and 50 deletions

View File

@@ -0,0 +1,403 @@
# 前端链接访问示例
## 概述
当用户访问链接页面时(如 `https://你的域名/ABC12345`),前端需要自动请求后端获取链接的详细信息,并根据状态显示相应的内容。
## 接口说明
### 1. 获取链接状态(主要接口)
```
GET /api/link/{codeNo}/status
```
**响应示例:**
```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. **实时更新**:可以定时刷新状态,显示剩余时间等
## 注意事项
1. **错误处理**:处理链接不存在、已过期等情况
2. **加载状态**:显示加载中的状态,提升用户体验
3. **响应式设计**:确保在不同设备上都能正常显示
4. **缓存策略**:可以适当缓存链接状态,减少请求次数
5. **实时更新**:对于进行中的任务,可以定时刷新状态