feat: 增强二维码和图片代理功能
主要修改: 1. 在QrProxyController中新增多个图片代理接口,包括首页、首次赏金、中途赏金和结束赏金图片的获取。 2. 更新LinkController中的链接状态查询逻辑,简化日志输出。 3. 在LinkStatusService中优化链接状态处理逻辑,增加对USING状态的过期检查。 4. 在ScriptClient中新增通用图片获取方法,支持从脚本端获取图片数据。 5. 更新SecurityConfig,允许公开访问二维码和游戏界面数据接口。 技术细节: - 新增GameInterfaceResponse DTO以支持游戏界面数据的返回格式。 - 通过脚本端接口实现图片的动态获取和链接状态的自动刷新。
This commit is contained in:
@@ -28,6 +28,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@@ -321,8 +322,7 @@ public Mono<Boolean> deleteLink(@PathVariable("codeNo") String codeNo, Authentic
|
||||
|
||||
return linkStatusService.getUserLinkStatus(linkId, actualCodeNo)
|
||||
.doOnSuccess(response -> {
|
||||
log.info("用户端链接状态查询成功: status={}, view={}, needRefresh={}",
|
||||
response.getStatus(), response.getView(), response.getNeedRefresh());
|
||||
log.info("用户端链接状态查询成功: status={}", response.getStatus());
|
||||
})
|
||||
.doOnError(error -> {
|
||||
log.error("用户端链接状态查询失败: {}", error.getMessage(), error);
|
||||
@@ -407,7 +407,15 @@ public Mono<Boolean> deleteLink(@PathVariable("codeNo") String codeNo, Authentic
|
||||
})
|
||||
.onErrorResume(error -> {
|
||||
log.error("获取设备 {} 二维码失败: {}", deviceId, error.getMessage(), error);
|
||||
return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
|
||||
|
||||
// 如果是404错误,返回404;其他错误返回500
|
||||
if (error instanceof WebClientResponseException.NotFound) {
|
||||
log.warn("设备 {} 的二维码文件不存在,返回404", deviceId);
|
||||
return Mono.just(ResponseEntity.status(HttpStatus.NOT_FOUND).build());
|
||||
} else {
|
||||
log.error("获取设备 {} 二维码时发生系统错误", deviceId);
|
||||
return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package com.gameplatform.server.controller.link;
|
||||
|
||||
import com.gameplatform.server.mapper.agent.LinkBatchMapper;
|
||||
import com.gameplatform.server.mapper.agent.LinkTaskMapper;
|
||||
import com.gameplatform.server.model.dto.link.GameInterfaceResponse;
|
||||
import com.gameplatform.server.model.entity.agent.LinkBatch;
|
||||
import com.gameplatform.server.model.entity.agent.LinkTask;
|
||||
import com.gameplatform.server.service.external.ScriptClient;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -13,16 +21,29 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/link")
|
||||
@Tag(name = "二维码代理", description = "转发脚本端的二维码图片,避免混合内容")
|
||||
@Tag(name = "图片代理", description = "转发脚本端的图片,避免混合内容")
|
||||
public class QrProxyController {
|
||||
private static final Logger log = LoggerFactory.getLogger(QrProxyController.class);
|
||||
|
||||
private final ScriptClient scriptClient;
|
||||
private final String appBaseUrl;
|
||||
private final LinkTaskMapper linkTaskMapper;
|
||||
private final LinkBatchMapper linkBatchMapper;
|
||||
|
||||
public QrProxyController(ScriptClient scriptClient) {
|
||||
public QrProxyController(ScriptClient scriptClient,
|
||||
@Value("${app.base-url}") String appBaseUrl,
|
||||
LinkTaskMapper linkTaskMapper,
|
||||
LinkBatchMapper linkBatchMapper) {
|
||||
this.scriptClient = scriptClient;
|
||||
this.appBaseUrl = appBaseUrl;
|
||||
this.linkTaskMapper = linkTaskMapper;
|
||||
this.linkBatchMapper = linkBatchMapper;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{codeNo}/qr.png", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@@ -36,6 +57,140 @@ public class QrProxyController {
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=qr.png")
|
||||
.body(bytes));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{codeNo}/homepage.png", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@Operation(summary = "首次主页图片代理")
|
||||
public Mono<ResponseEntity<byte[]>> homepage(@PathVariable("codeNo") String codeNo) {
|
||||
String path = "/" + codeNo + "/首次主页.png";
|
||||
return scriptClient.getImagePng(path)
|
||||
.map(bytes -> ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS).cachePublic())
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=homepage.png")
|
||||
.body(bytes));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{codeNo}/first-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@Operation(summary = "首次赏金图片代理")
|
||||
public Mono<ResponseEntity<byte[]>> firstReward(@PathVariable("codeNo") String codeNo) {
|
||||
String path = "/" + codeNo + "/首次赏金.png";
|
||||
return scriptClient.getImagePng(path)
|
||||
.map(bytes -> ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS).cachePublic())
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=first-reward.png")
|
||||
.body(bytes));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{codeNo}/mid-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@Operation(summary = "中途赏金图片代理")
|
||||
public Mono<ResponseEntity<byte[]>> midReward(@PathVariable("codeNo") String codeNo) {
|
||||
String path = "/" + codeNo + "/中途赏金.png";
|
||||
return scriptClient.getImagePng(path)
|
||||
.map(bytes -> ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS).cachePublic())
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=mid-reward.png")
|
||||
.body(bytes));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{codeNo}/end-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@Operation(summary = "结束赏金图片代理")
|
||||
public Mono<ResponseEntity<byte[]>> endReward(@PathVariable("codeNo") String codeNo) {
|
||||
String path = "/" + codeNo + "/结束赏金.png";
|
||||
return scriptClient.getImagePng(path)
|
||||
.map(bytes -> ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS).cachePublic())
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=end-reward.png")
|
||||
.body(bytes));
|
||||
}
|
||||
|
||||
@GetMapping("/{codeNo}/images")
|
||||
@Operation(summary = "获取所有图片代理链接")
|
||||
public ResponseEntity<Map<String, String>> getImageLinks(@PathVariable("codeNo") String codeNo) {
|
||||
Map<String, String> imageLinks = new HashMap<>();
|
||||
|
||||
// 添加二维码链接
|
||||
imageLinks.put("qrCode", appBaseUrl + "/api/link/" + codeNo + "/qr.png");
|
||||
|
||||
// 添加其他图片链接
|
||||
imageLinks.put("homepage", appBaseUrl + "/api/link/" + codeNo + "/homepage.png");
|
||||
imageLinks.put("firstReward", appBaseUrl + "/api/link/" + codeNo + "/first-reward.png");
|
||||
imageLinks.put("midReward", appBaseUrl + "/api/link/" + codeNo + "/mid-reward.png");
|
||||
imageLinks.put("endReward", appBaseUrl + "/api/link/" + codeNo + "/end-reward.png");
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(CacheControl.maxAge(5, TimeUnit.MINUTES).cachePublic())
|
||||
.body(imageLinks);
|
||||
}
|
||||
|
||||
@GetMapping("/{codeNo}/game-interface")
|
||||
@Operation(summary = "获取游戏界面数据", description = "返回四张图片链接和总点数信息")
|
||||
public ResponseEntity<GameInterfaceResponse> getGameInterface(@PathVariable("codeNo") String codeNo) {
|
||||
log.info("获取游戏界面数据: codeNo={}", codeNo);
|
||||
|
||||
try {
|
||||
// 查询链接任务
|
||||
LinkTask linkTask = linkTaskMapper.findByCodeNo(codeNo);
|
||||
if (linkTask == null) {
|
||||
log.warn("链接任务不存在: codeNo={}", codeNo);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// 查询批次信息
|
||||
LinkBatch linkBatch = linkBatchMapper.findById(linkTask.getBatchId());
|
||||
if (linkBatch == null) {
|
||||
log.warn("批次信息不存在: batchId={}", linkTask.getBatchId());
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// 构建响应对象
|
||||
GameInterfaceResponse response = new GameInterfaceResponse();
|
||||
response.setCodeNo(codeNo);
|
||||
response.setQuantity(linkBatch.getQuantity());
|
||||
response.setTimes(linkBatch.getTimes());
|
||||
response.setTotalPoints(linkBatch.getQuantity() * linkBatch.getTimes());
|
||||
|
||||
// 设置游戏区域信息
|
||||
response.setRegion(linkTask.getRegion());
|
||||
response.setRegionDesc(getRegionDescription(linkTask.getRegion()));
|
||||
|
||||
// 设置图片链接
|
||||
response.setQrCodeUrl(appBaseUrl + "/api/link/" + codeNo + "/qr.png");
|
||||
response.setHomepageUrl(appBaseUrl + "/api/link/" + codeNo + "/homepage.png");
|
||||
response.setFirstRewardUrl(appBaseUrl + "/api/link/" + codeNo + "/first-reward.png");
|
||||
response.setMidRewardUrl(appBaseUrl + "/api/link/" + codeNo + "/mid-reward.png");
|
||||
response.setEndRewardUrl(appBaseUrl + "/api/link/" + codeNo + "/end-reward.png");
|
||||
|
||||
log.info("游戏界面数据构建完成: codeNo={}, totalPoints={}", codeNo, response.getTotalPoints());
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(CacheControl.maxAge(2, TimeUnit.MINUTES).cachePublic())
|
||||
.body(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取游戏界面数据失败: codeNo={}, error={}", codeNo, e.getMessage(), e);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取区域描述
|
||||
*/
|
||||
private String getRegionDescription(String region) {
|
||||
if (region == null) {
|
||||
return "未选择区域";
|
||||
}
|
||||
switch (region) {
|
||||
case "Q":
|
||||
return "QQ区";
|
||||
case "V":
|
||||
return "微信区";
|
||||
default:
|
||||
return "未知区域";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user