Files
game_server/GAME_INTERFACE_COMPLETED_TIME_UPDATE.md

362 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
**状态**: ✅ 已完成