feat: 更新公告和链接状态接口,增强参数校验,支持跳转链接最大长度为5000字符,添加异步保存完成图片功能,优化接口文档和数据库结构
This commit is contained in:
305
API_COMPLETION_TIMESTAMP.md
Normal file
305
API_COMPLETION_TIMESTAMP.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# 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时间戳(秒级)
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
Reference in New Issue
Block a user