feat: 更新公告和链接状态接口,增强参数校验,支持跳转链接最大长度为5000字符,添加异步保存完成图片功能,优化接口文档和数据库结构
This commit is contained in:
361
GAME_INTERFACE_COMPLETED_TIME_UPDATE.md
Normal file
361
GAME_INTERFACE_COMPLETED_TIME_UPDATE.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# Game Interface 接口新增完成时间
|
||||
|
||||
## ✅ 修改完成
|
||||
|
||||
在 `/api/link/{codeNo}/game-interface` 接口响应中新增了 `completedAt`(完成时间)和 `status`(任务状态)字段。
|
||||
|
||||
## 📦 修改的文件
|
||||
|
||||
1. **GameInterfaceResponse.java** - 响应DTO
|
||||
- 新增 `status` 字段(任务状态)
|
||||
- 新增 `completedAt` 字段(完成时间)
|
||||
|
||||
2. **QrProxyController.java** - 控制器
|
||||
- 设置 `status` 字段
|
||||
- 仅当任务完成时设置 `completedAt` 字段
|
||||
|
||||
## 📊 接口响应示例
|
||||
|
||||
### 任务进行中(NEW / USING / LOGGED_IN)
|
||||
|
||||
```json
|
||||
{
|
||||
"codeNo": "MYNM5JHA",
|
||||
"totalPoints": 1000,
|
||||
"quantity": 100,
|
||||
"times": 10,
|
||||
"region": "Q",
|
||||
"regionDesc": "QQ区",
|
||||
"machineId": "rr3",
|
||||
"completedPoints": null,
|
||||
"status": "LOGGED_IN",
|
||||
"completedAt": null,
|
||||
"qrCodeUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/qr.png",
|
||||
"homepageUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/homepage.png",
|
||||
"firstRewardUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/first-reward.png",
|
||||
"midRewardUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/mid-reward.png",
|
||||
"endRewardUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/end-reward.png",
|
||||
"progressDisplayFormat": "percent"
|
||||
}
|
||||
```
|
||||
|
||||
### 任务已完成(COMPLETED)✨
|
||||
|
||||
```json
|
||||
{
|
||||
"codeNo": "MYNM5JHA",
|
||||
"totalPoints": 1000,
|
||||
"quantity": 100,
|
||||
"times": 10,
|
||||
"region": "Q",
|
||||
"regionDesc": "QQ区",
|
||||
"machineId": "rr3",
|
||||
"completedPoints": 1000,
|
||||
"status": "COMPLETED",
|
||||
"completedAt": 1730644245,
|
||||
"qrCodeUrl": "https://uzi1.cn/api/link/image/MYNM5JHA/qr.png",
|
||||
"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",
|
||||
"progressDisplayFormat": "percent"
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 新增字段说明
|
||||
|
||||
### status(任务状态)
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| `status` | String | 任务当前状态 | `"COMPLETED"` |
|
||||
|
||||
**可能的值**:
|
||||
- `NEW`: 新建
|
||||
- `USING`: 使用中
|
||||
- `LOGGED_IN`: 已登录
|
||||
- `COMPLETED`: 已完成 ✨
|
||||
- `REFUNDED`: 已退款
|
||||
- `EXPIRED`: 已过期
|
||||
|
||||
---
|
||||
|
||||
### completedAt(完成时间戳)
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| `completedAt` | Long | 任务完成时间戳(秒级) | `1730644245` |
|
||||
|
||||
**特点**:
|
||||
- ✅ 仅当 `status` 为 `COMPLETED` 时有值
|
||||
- ✅ 使用 Unix 时间戳(秒级,10位数字)
|
||||
- ✅ 其他状态下为 `null`
|
||||
- ✅ 可直接用于前端时间处理
|
||||
|
||||
---
|
||||
|
||||
## 🔧 实现逻辑
|
||||
|
||||
```java
|
||||
// 设置状态
|
||||
response.setStatus(linkTask.getStatus());
|
||||
|
||||
// 设置完成时间戳-秒级(仅当任务完成时)
|
||||
if ("COMPLETED".equals(linkTask.getStatus()) && linkTask.getUpdatedAt() != null) {
|
||||
// 转换为秒级时间戳
|
||||
long epochSecond = linkTask.getUpdatedAt()
|
||||
.atZone(java.time.ZoneId.systemDefault())
|
||||
.toEpochSecond();
|
||||
response.setCompletedAt(epochSecond);
|
||||
}
|
||||
```
|
||||
|
||||
## 💡 前端使用示例
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
```javascript
|
||||
// 获取游戏界面数据
|
||||
fetch(`/api/link/${codeNo}/game-interface`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log('任务状态:', data.status);
|
||||
|
||||
// 判断任务是否完成
|
||||
if (data.status === 'COMPLETED' && data.completedAt) {
|
||||
console.log('完成时间戳:', data.completedAt);
|
||||
|
||||
// 将秒级时间戳转换为毫秒(JavaScript Date需要毫秒)
|
||||
const completedTime = new Date(data.completedAt * 1000);
|
||||
console.log('格式化时间:', completedTime.toLocaleString('zh-CN'));
|
||||
// 输出: 2025/11/3 20:30:45
|
||||
|
||||
// 计算完成了多久
|
||||
const now = new Date();
|
||||
const diffMs = now - completedTime;
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
console.log(`${diffMins} 分钟前完成`);
|
||||
|
||||
// 显示完成图片
|
||||
document.getElementById('homepage').src = data.homepageUrl;
|
||||
document.getElementById('firstReward').src = data.firstRewardUrl;
|
||||
document.getElementById('midReward').src = data.midRewardUrl;
|
||||
document.getElementById('endReward').src = data.endRewardUrl;
|
||||
} else {
|
||||
console.log('任务进行中...');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Vue/React 组件示例
|
||||
|
||||
```javascript
|
||||
// Vue 3 Composition API
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
const gameData = ref(null);
|
||||
|
||||
// 格式化完成时间
|
||||
const formattedCompletedTime = computed(() => {
|
||||
if (gameData.value?.completedAt) {
|
||||
// 秒级时间戳转毫秒
|
||||
const date = new Date(gameData.value.completedAt * 1000);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// 计算完成多久
|
||||
const completedAgo = computed(() => {
|
||||
if (gameData.value?.completedAt) {
|
||||
// 秒级时间戳转毫秒
|
||||
const completedTime = new Date(gameData.value.completedAt * 1000);
|
||||
const now = new Date();
|
||||
const diffMs = now - completedTime;
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
|
||||
if (diffMins < 1) return '刚刚完成';
|
||||
if (diffMins < 60) return `${diffMins} 分钟前`;
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
if (diffHours < 24) return `${diffHours} 小时前`;
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
return `${diffDays} 天前`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
```
|
||||
|
||||
## 📱 UI 显示建议
|
||||
|
||||
### 完成状态显示
|
||||
|
||||
```html
|
||||
<!-- 任务完成提示 -->
|
||||
<div v-if="gameData.status === 'COMPLETED'" class="completion-banner">
|
||||
<span class="status-badge">✅ 已完成</span>
|
||||
<span class="completion-time">
|
||||
完成时间: {{ formattedCompletedTime }}
|
||||
</span>
|
||||
<span class="time-ago">
|
||||
({{ completedAgo }})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 完成点数 -->
|
||||
<div class="points-display">
|
||||
<span>完成点数: {{ gameData.completedPoints }} / {{ gameData.totalPoints }}</span>
|
||||
<progress :value="gameData.completedPoints" :max="gameData.totalPoints"></progress>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 不同状态的UI提示
|
||||
|
||||
```javascript
|
||||
const statusDisplay = {
|
||||
'NEW': { text: '等待开始', color: 'gray', icon: '⏳' },
|
||||
'USING': { text: '使用中', color: 'blue', icon: '🎮' },
|
||||
'LOGGED_IN': { text: '已登录', color: 'green', icon: '✓' },
|
||||
'COMPLETED': { text: '已完成', color: 'success', icon: '✅' },
|
||||
'REFUNDED': { text: '已退款', color: 'warning', icon: '↩️' },
|
||||
'EXPIRED': { text: '已过期', color: 'danger', icon: '⏰' }
|
||||
};
|
||||
|
||||
const currentStatus = statusDisplay[gameData.status];
|
||||
```
|
||||
|
||||
## 🎨 样式建议
|
||||
|
||||
```css
|
||||
.completion-banner {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.completion-time {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.time-ago {
|
||||
opacity: 0.8;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.points-display {
|
||||
background: #f8f9fa;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.points-display progress {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 字段对比
|
||||
|
||||
| 场景 | status | completedAt | completedPoints | 图片URL前缀 |
|
||||
|------|--------|-------------|-----------------|-------------|
|
||||
| 新建任务 | `NEW` | `null` | `null` | `/api/link/image/` |
|
||||
| 使用中 | `USING` | `null` | `null` | `/api/link/image/` |
|
||||
| 已登录 | `LOGGED_IN` | `null` | 可能有值 | `/api/link/image/` |
|
||||
| 已完成 ✨ | `COMPLETED` | **有值** | **有值** | `/api/link/completion/` |
|
||||
| 已退款 | `REFUNDED` | `null` | 可能有值 | `/api/link/image/` |
|
||||
| 已过期 | `EXPIRED` | `null` | `null` | `/api/link/image/` |
|
||||
|
||||
## ⚙️ 时间戳格式说明
|
||||
|
||||
### Unix 时间戳(秒级)
|
||||
```
|
||||
1730644245
|
||||
│
|
||||
└─ 10位数字,表示自1970-01-01 00:00:00 UTC以来的秒数
|
||||
```
|
||||
|
||||
**示例值**:
|
||||
- `1730644245` = 2025-11-03 20:30:45 (北京时间)
|
||||
|
||||
### 解析示例
|
||||
|
||||
```javascript
|
||||
// JavaScript - 秒级时间戳需要乘以1000转换为毫秒
|
||||
const completedAt = 1730644245;
|
||||
const date = new Date(completedAt * 1000); // 注意:乘以1000
|
||||
|
||||
console.log(date.toLocaleString('zh-CN'));
|
||||
// 输出: 2025/11/3 20:30:45
|
||||
|
||||
console.log(date.toLocaleDateString('zh-CN'));
|
||||
// 输出: 2025/11/3
|
||||
|
||||
console.log(date.toLocaleTimeString('zh-CN'));
|
||||
// 输出: 20:30:45
|
||||
|
||||
// 或者使用时间库(如 dayjs)
|
||||
import dayjs from 'dayjs';
|
||||
console.log(dayjs.unix(completedAt).format('YYYY-MM-DD HH:mm:ss'));
|
||||
// 输出: 2025-11-03 20:30:45
|
||||
```
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 1. 任务进行中
|
||||
```bash
|
||||
curl "http://localhost:18080/api/link/MYNM5JHA/game-interface" | jq .
|
||||
|
||||
# 预期结果
|
||||
{
|
||||
"status": "LOGGED_IN",
|
||||
"completedAt": null,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 任务完成后
|
||||
```bash
|
||||
curl "http://localhost:18080/api/link/MYNM5JHA/game-interface" | jq .
|
||||
|
||||
# 预期结果
|
||||
{
|
||||
"status": "COMPLETED",
|
||||
"completedAt": 1730644245,
|
||||
"completedPoints": 1000,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## 📖 相关文档
|
||||
|
||||
- 完成图片保存功能: `COMPLETION_IMAGE_FEATURE_SUMMARY.md`
|
||||
- 图片URL优化: `GAME_INTERFACE_IMAGE_UPDATE.md`
|
||||
- 所有完成触发点: `COMPLETION_IMAGE_ALL_TRIGGERS.md`
|
||||
|
||||
---
|
||||
|
||||
**更新时间**: 2025-11-03
|
||||
**版本**: v1.3.0
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
Reference in New Issue
Block a user