feat: 更新公告和链接状态接口,增强参数校验,支持跳转链接最大长度为5000字符,添加异步保存完成图片功能,优化接口文档和数据库结构

This commit is contained in:
yahaozhang
2025-11-03 20:56:34 +08:00
parent f43320138a
commit cadf8d98cb
40 changed files with 3148 additions and 17 deletions

305
API_COMPLETION_TIMESTAMP.md Normal file
View 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时间戳秒级
**状态**: ✅ 已完成