306 lines
7.0 KiB
Markdown
306 lines
7.0 KiB
Markdown
# Game Interface API - 完成时间戳快速参考
|
||
|
||
## 📡 接口
|
||
|
||
```
|
||
GET /api/link/{codeNo}/game-interface
|
||
```
|
||
|
||
## 📊 响应字段(新增)
|
||
|
||
### completedAt - 完成时间戳
|
||
|
||
| 字段 | 类型 | 说明 | 示例 |
|
||
|------|------|------|------|
|
||
| `completedAt` | `Long` | 秒级Unix时间戳 | `1730644245` |
|
||
| `status` | `String` | 任务状态 | `"COMPLETED"` |
|
||
|
||
## 💡 特点
|
||
|
||
- ✅ **秒级时间戳**:10位数字,如 `1730644245`
|
||
- ✅ **仅完成时有值**:只有 `status === "COMPLETED"` 时才有值
|
||
- ✅ **其他状态为null**:未完成的任务返回 `null`
|
||
|
||
## 🎯 响应示例
|
||
|
||
### 任务完成时
|
||
|
||
```json
|
||
{
|
||
"codeNo": "MYNM5JHA",
|
||
"status": "COMPLETED",
|
||
"completedAt": 1730644245,
|
||
"completedPoints": 1000,
|
||
"totalPoints": 1000,
|
||
"homepageUrl": "https://uzi1.cn/api/link/completion/MYNM5JHA/homepage.png",
|
||
"firstRewardUrl": "https://uzi1.cn/api/link/completion/MYNM5JHA/first-reward.png",
|
||
"midRewardUrl": "https://uzi1.cn/api/link/completion/MYNM5JHA/mid-reward.png",
|
||
"endRewardUrl": "https://uzi1.cn/api/link/completion/MYNM5JHA/end-reward.png",
|
||
...
|
||
}
|
||
```
|
||
|
||
### 任务进行中
|
||
|
||
```json
|
||
{
|
||
"codeNo": "MYNM5JHA",
|
||
"status": "LOGGED_IN",
|
||
"completedAt": null,
|
||
"completedPoints": null,
|
||
"homepageUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/homepage.png",
|
||
...
|
||
}
|
||
```
|
||
|
||
## 🔧 前端使用(JavaScript)
|
||
|
||
### 基础用法
|
||
|
||
```javascript
|
||
fetch(`/api/link/${codeNo}/game-interface`)
|
||
.then(res => res.json())
|
||
.then(data => {
|
||
if (data.status === 'COMPLETED' && data.completedAt) {
|
||
// 秒级时间戳需要 * 1000 转换为毫秒
|
||
const completedTime = new Date(data.completedAt * 1000);
|
||
console.log('完成时间:', completedTime.toLocaleString('zh-CN'));
|
||
}
|
||
});
|
||
```
|
||
|
||
### 格式化时间
|
||
|
||
```javascript
|
||
// 方法1: 原生 JavaScript
|
||
const formatTimestamp = (timestamp) => {
|
||
const date = new Date(timestamp * 1000); // 秒转毫秒
|
||
return date.toLocaleString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit'
|
||
});
|
||
};
|
||
|
||
console.log(formatTimestamp(1730644245));
|
||
// 输出: 2025/11/03 20:30:45
|
||
|
||
// 方法2: 使用 dayjs
|
||
import dayjs from 'dayjs';
|
||
console.log(dayjs.unix(1730644245).format('YYYY-MM-DD HH:mm:ss'));
|
||
// 输出: 2025-11-03 20:30:45
|
||
|
||
// 方法3: 使用 moment
|
||
import moment from 'moment';
|
||
console.log(moment.unix(1730644245).format('YYYY-MM-DD HH:mm:ss'));
|
||
// 输出: 2025-11-03 20:30:45
|
||
```
|
||
|
||
### 计算完成多久
|
||
|
||
```javascript
|
||
const getTimeAgo = (timestamp) => {
|
||
const completedTime = new Date(timestamp * 1000);
|
||
const now = new Date();
|
||
const diffSeconds = Math.floor((now - completedTime) / 1000);
|
||
|
||
if (diffSeconds < 60) return `${diffSeconds} 秒前`;
|
||
|
||
const diffMins = Math.floor(diffSeconds / 60);
|
||
if (diffMins < 60) return `${diffMins} 分钟前`;
|
||
|
||
const diffHours = Math.floor(diffMins / 60);
|
||
if (diffHours < 24) return `${diffHours} 小时前`;
|
||
|
||
const diffDays = Math.floor(diffHours / 24);
|
||
return `${diffDays} 天前`;
|
||
};
|
||
|
||
console.log(getTimeAgo(1730644245));
|
||
// 输出: 5 分钟前
|
||
```
|
||
|
||
## 🎨 Vue 组件示例
|
||
|
||
```vue
|
||
<template>
|
||
<div v-if="gameData">
|
||
<!-- 状态徽章 -->
|
||
<div class="status-badge" :class="statusClass">
|
||
{{ statusText }}
|
||
</div>
|
||
|
||
<!-- 完成信息(仅完成时显示) -->
|
||
<div v-if="gameData.status === 'COMPLETED'" class="completion-info">
|
||
<div class="completion-time">
|
||
<span class="label">完成时间:</span>
|
||
<span class="value">{{ formattedCompletedTime }}</span>
|
||
</div>
|
||
<div class="time-ago">{{ completedAgo }}</div>
|
||
<div class="points">
|
||
完成点数: {{ gameData.completedPoints }} / {{ gameData.totalPoints }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue';
|
||
|
||
const gameData = ref(null);
|
||
|
||
// 状态类名
|
||
const statusClass = computed(() => {
|
||
const classMap = {
|
||
'NEW': 'status-new',
|
||
'USING': 'status-using',
|
||
'LOGGED_IN': 'status-logged-in',
|
||
'COMPLETED': 'status-completed',
|
||
'REFUNDED': 'status-refunded',
|
||
'EXPIRED': 'status-expired'
|
||
};
|
||
return classMap[gameData.value?.status] || 'status-default';
|
||
});
|
||
|
||
// 状态文本
|
||
const statusText = computed(() => {
|
||
const textMap = {
|
||
'NEW': '等待开始',
|
||
'USING': '使用中',
|
||
'LOGGED_IN': '游戏中',
|
||
'COMPLETED': '已完成',
|
||
'REFUNDED': '已退款',
|
||
'EXPIRED': '已过期'
|
||
};
|
||
return textMap[gameData.value?.status] || '未知';
|
||
});
|
||
|
||
// 格式化完成时间
|
||
const formattedCompletedTime = computed(() => {
|
||
if (gameData.value?.completedAt) {
|
||
const date = new Date(gameData.value.completedAt * 1000);
|
||
return date.toLocaleString('zh-CN');
|
||
}
|
||
return '';
|
||
});
|
||
|
||
// 完成多久前
|
||
const completedAgo = computed(() => {
|
||
if (!gameData.value?.completedAt) return '';
|
||
|
||
const completedTime = new Date(gameData.value.completedAt * 1000);
|
||
const now = new Date();
|
||
const diffSeconds = Math.floor((now - completedTime) / 1000);
|
||
|
||
if (diffSeconds < 60) return `${diffSeconds} 秒前`;
|
||
const diffMins = Math.floor(diffSeconds / 60);
|
||
if (diffMins < 60) return `${diffMins} 分钟前`;
|
||
const diffHours = Math.floor(diffMins / 60);
|
||
if (diffHours < 24) return `${diffHours} 小时前`;
|
||
const diffDays = Math.floor(diffHours / 24);
|
||
return `${diffDays} 天前`;
|
||
});
|
||
|
||
// 加载数据
|
||
const loadGameData = async (codeNo) => {
|
||
const res = await fetch(`/api/link/${codeNo}/game-interface`);
|
||
gameData.value = await res.json();
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.status-badge {
|
||
display: inline-block;
|
||
padding: 6px 16px;
|
||
border-radius: 20px;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.status-completed {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
}
|
||
|
||
.completion-info {
|
||
margin-top: 16px;
|
||
padding: 16px;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.completion-time {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.time-ago {
|
||
color: #6c757d;
|
||
font-size: 13px;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
## 🔍 时间戳对照表
|
||
|
||
| 时间戳 | 日期时间(北京时间) |
|
||
|--------|---------------------|
|
||
| `1730644245` | 2025-11-03 20:30:45 |
|
||
| `1730640000` | 2025-11-03 19:20:00 |
|
||
| `1730635200` | 2025-11-03 18:00:00 |
|
||
|
||
## 🧪 快速测试
|
||
|
||
```bash
|
||
# 测试接口
|
||
curl "http://localhost:18080/api/link/MYNM5JHA/game-interface" | jq '.completedAt'
|
||
|
||
# 转换时间戳(Linux/Mac)
|
||
date -r 1730644245
|
||
# 或
|
||
date -d @1730644245
|
||
```
|
||
|
||
## ⚡ 常见时间处理库
|
||
|
||
### JavaScript
|
||
|
||
```bash
|
||
# dayjs (推荐 - 轻量)
|
||
npm install dayjs
|
||
|
||
# moment.js (功能强大)
|
||
npm install moment
|
||
|
||
# date-fns (函数式)
|
||
npm install date-fns
|
||
```
|
||
|
||
### 使用 dayjs
|
||
|
||
```javascript
|
||
import dayjs from 'dayjs';
|
||
|
||
// 格式化
|
||
dayjs.unix(1730644245).format('YYYY-MM-DD HH:mm:ss');
|
||
|
||
// 相对时间
|
||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||
import 'dayjs/locale/zh-cn';
|
||
dayjs.extend(relativeTime);
|
||
dayjs.locale('zh-cn');
|
||
dayjs.unix(1730644245).fromNow(); // "5分钟前"
|
||
```
|
||
|
||
---
|
||
|
||
**更新时间**: 2025-11-03
|
||
**格式**: Unix时间戳(秒级)
|
||
**状态**: ✅ 已完成
|
||
|