feat: 更新二维码和图片访问接口逻辑

主要修改:
1. 在QrProxyController中引入LinkStatusService,通过codeNo查询对应的machineId,优化二维码和图片的获取逻辑。
2. 更新所有相关的图片访问路径,确保使用新的/image/{codeNo}格式。
3. 在GameInterfaceResponse中新增machineId和completedPoints字段,提供更详细的游戏界面数据。
4. 更新SecurityConfig,允许/image/**路径的公开访问。

技术细节:
- 通过优化接口逻辑和数据结构,提升了系统的灵活性和用户体验,同时确保了安全性和可维护性。
This commit is contained in:
zyh
2025-08-28 23:30:58 +08:00
parent 1d72bc4c5a
commit de4700abe6
8 changed files with 91 additions and 24 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

View File

@@ -6,6 +6,7 @@ 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 com.gameplatform.server.service.link.LinkStatusService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
@@ -32,24 +33,34 @@ public class QrProxyController {
private static final Logger log = LoggerFactory.getLogger(QrProxyController.class);
private final ScriptClient scriptClient;
private final LinkStatusService linkStatusService;
private final String appBaseUrl;
private final LinkTaskMapper linkTaskMapper;
private final LinkBatchMapper linkBatchMapper;
public QrProxyController(ScriptClient scriptClient,
LinkStatusService linkStatusService,
@Value("${app.base-url}") String appBaseUrl,
LinkTaskMapper linkTaskMapper,
LinkBatchMapper linkBatchMapper) {
this.scriptClient = scriptClient;
this.linkStatusService = linkStatusService;
this.appBaseUrl = appBaseUrl;
this.linkTaskMapper = linkTaskMapper;
this.linkBatchMapper = linkBatchMapper;
}
@GetMapping(value = "/{codeNo}/qr.png", produces = MediaType.IMAGE_PNG_VALUE)
@GetMapping(value = "/image/{codeNo}/qr.png", produces = MediaType.IMAGE_PNG_VALUE)
@Operation(summary = "二维码图片代理")
public Mono<ResponseEntity<byte[]>> qr(@PathVariable("codeNo") String codeNo) {
String path = "/" + codeNo + "/二维码.png";
// 通过codeNo查询machineId
String machineId = linkStatusService.getMechainIdByCode(codeNo);
if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo);
return Mono.just(ResponseEntity.notFound().build());
}
String path = "/" + machineId + "/二维码.png";
return scriptClient.getQrPng(path)
.map(bytes -> ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
@@ -58,10 +69,17 @@ public class QrProxyController {
.body(bytes));
}
@GetMapping(value = "/{codeNo}/homepage.png", produces = MediaType.IMAGE_PNG_VALUE)
@GetMapping(value = "/image/{codeNo}/homepage.png", produces = MediaType.IMAGE_PNG_VALUE)
@Operation(summary = "首次主页图片代理")
public Mono<ResponseEntity<byte[]>> homepage(@PathVariable("codeNo") String codeNo) {
String path = "/" + codeNo + "/首次主页.png";
// 通过codeNo查询machineId
String machineId = linkStatusService.getMechainIdByCode(codeNo);
if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo);
return Mono.just(ResponseEntity.notFound().build());
}
String path = "/" + machineId + "/首次主页.png";
return scriptClient.getImagePng(path)
.map(bytes -> ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
@@ -70,10 +88,17 @@ public class QrProxyController {
.body(bytes));
}
@GetMapping(value = "/{codeNo}/first-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
@GetMapping(value = "/image/{codeNo}/first-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
@Operation(summary = "首次赏金图片代理")
public Mono<ResponseEntity<byte[]>> firstReward(@PathVariable("codeNo") String codeNo) {
String path = "/" + codeNo + "/首次赏金.png";
// 通过codeNo查询machineId
String machineId = linkStatusService.getMechainIdByCode(codeNo);
if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo);
return Mono.just(ResponseEntity.notFound().build());
}
String path = "/" + machineId + "/首次赏金.png";
return scriptClient.getImagePng(path)
.map(bytes -> ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
@@ -82,10 +107,17 @@ public class QrProxyController {
.body(bytes));
}
@GetMapping(value = "/{codeNo}/mid-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
@GetMapping(value = "/image/{codeNo}/mid-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
@Operation(summary = "中途赏金图片代理")
public Mono<ResponseEntity<byte[]>> midReward(@PathVariable("codeNo") String codeNo) {
String path = "/" + codeNo + "/中途赏金.png";
// 通过codeNo查询machineId
String machineId = linkStatusService.getMechainIdByCode(codeNo);
if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo);
return Mono.just(ResponseEntity.notFound().build());
}
String path = "/" + machineId + "/中途赏金.png";
return scriptClient.getImagePng(path)
.map(bytes -> ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
@@ -94,10 +126,17 @@ public class QrProxyController {
.body(bytes));
}
@GetMapping(value = "/{codeNo}/end-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
@GetMapping(value = "/image/{codeNo}/end-reward.png", produces = MediaType.IMAGE_PNG_VALUE)
@Operation(summary = "结束赏金图片代理")
public Mono<ResponseEntity<byte[]>> endReward(@PathVariable("codeNo") String codeNo) {
String path = "/" + codeNo + "/结束赏金.png";
// 通过codeNo查询machineId
String machineId = linkStatusService.getMechainIdByCode(codeNo);
if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo);
return Mono.just(ResponseEntity.notFound().build());
}
String path = "/" + machineId + "/结束赏金.png";
return scriptClient.getImagePng(path)
.map(bytes -> ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
@@ -112,13 +151,13 @@ public class QrProxyController {
Map<String, String> imageLinks = new HashMap<>();
// 添加二维码链接
imageLinks.put("qrCode", appBaseUrl + "/api/link/" + codeNo + "/qr.png");
imageLinks.put("qrCode", appBaseUrl + "/api/link/image/" + 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");
imageLinks.put("homepage", appBaseUrl + "/api/link/image/" + codeNo + "/homepage.png");
imageLinks.put("firstReward", appBaseUrl + "/api/link/image/" + codeNo + "/first-reward.png");
imageLinks.put("midReward", appBaseUrl + "/api/link/image/" + codeNo + "/mid-reward.png");
imageLinks.put("endReward", appBaseUrl + "/api/link/image/" + codeNo + "/end-reward.png");
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(5, TimeUnit.MINUTES).cachePublic())
@@ -148,20 +187,22 @@ public class QrProxyController {
// 构建响应对象
GameInterfaceResponse response = new GameInterfaceResponse();
response.setCodeNo(codeNo);
response.setMachineId(linkTask.getMachineId());
response.setQuantity(linkBatch.getQuantity());
response.setTimes(linkBatch.getTimes());
response.setTotalPoints(linkBatch.getQuantity() * linkBatch.getTimes());
response.setCompletedPoints(linkTask.getCompletedPoints());
// 设置游戏区域信息
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");
response.setQrCodeUrl(appBaseUrl + "/api/link/image/" + codeNo + "/qr.png");
response.setHomepageUrl(appBaseUrl + "/api/link/image/" + codeNo + "/homepage.png");
response.setFirstRewardUrl(appBaseUrl + "/api/link/image/" + codeNo + "/first-reward.png");
response.setMidRewardUrl(appBaseUrl + "/api/link/image/" + codeNo + "/mid-reward.png");
response.setEndRewardUrl(appBaseUrl + "/api/link/image/" + codeNo + "/end-reward.png");
log.info("游戏界面数据构建完成: codeNo={}, totalPoints={}", codeNo, response.getTotalPoints());

View File

@@ -8,7 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "游戏界面数据")
public class GameInterfaceResponse {
@Schema(description = "设备编号")
@Schema(description = "链接编号")
private String codeNo;
@Schema(description = "总点数 (quantity * times)")
@@ -41,6 +41,12 @@ public class GameInterfaceResponse {
@Schema(description = "结束赏金图片链接")
private String endRewardUrl;
@Schema(description = "机器编号")
private String machineId;
@Schema(description = "已经完成的点数")
private Integer completedPoints;
public String getCodeNo() {
return codeNo;
}
@@ -128,4 +134,20 @@ public class GameInterfaceResponse {
public void setEndRewardUrl(String endRewardUrl) {
this.endRewardUrl = endRewardUrl;
}
public String getMachineId() {
return machineId;
}
public void setMachineId(String machineId) {
this.machineId = machineId;
}
public Integer getCompletedPoints() {
return completedPoints;
}
public void setCompletedPoints(Integer completedPoints) {
this.completedPoints = completedPoints;
}
}

View File

@@ -47,6 +47,8 @@ public class SecurityConfig {
.pathMatchers(HttpMethod.GET, "/api/link/poll-login").permitAll() // 用户端轮询登录接口,公开访问
.pathMatchers(HttpMethod.GET, "/api/link/qr/**").permitAll() // 二维码获取接口,公开访问
.pathMatchers(HttpMethod.HEAD, "/api/link/qr/**").permitAll() // 二维码HEAD请求公开访问
.pathMatchers(HttpMethod.GET, "/api/link/image/**").permitAll() // 图片访问接口,公开访问
.pathMatchers(HttpMethod.HEAD, "/api/link/image/**").permitAll() // 图片HEAD请求公开访问
.pathMatchers(HttpMethod.GET, "/api/link/*/game-interface").permitAll() // 游戏界面数据接口,公开访问
.pathMatchers("/api/link/**").authenticated() // 其他链接接口需要认证
.anyExchange().permitAll() // 其他接口后续再收紧
@@ -72,6 +74,8 @@ public class SecurityConfig {
log.info(" * GET /api/link/poll-login -> 允许所有 (用户端公开接口)");
log.info(" * GET /api/link/qr/** -> 允许所有 (二维码获取接口)");
log.info(" * HEAD /api/link/qr/** -> 允许所有 (二维码HEAD请求)");
log.info(" * GET /api/link/image/** -> 允许所有 (图片访问接口)");
log.info(" * HEAD /api/link/image/** -> 允许所有 (图片HEAD请求)");
log.info(" * GET /api/link/*/game-interface -> 允许所有 (游戏界面数据接口)");
log.info(" * /api/link/** -> 需要认证");
log.info(" * 其他路径 -> 允许所有");

View File

@@ -259,7 +259,7 @@ public class ScriptClient {
*/
public String getProxyQrCodeUrl(String proxyCode) {
long timestamp = System.currentTimeMillis();
return String.format("%s/api/link/qr/%s?t=%d", appBaseUrl, proxyCode, timestamp);
return String.format("%s/api/link/image/%s/qr.png?t=%d", appBaseUrl, proxyCode, timestamp);
}
/**

View File

@@ -70,8 +70,8 @@ script:
# 服务器配置
app:
# base-url: "https://2.uzi0.cc" # 生产环境需要配置为实际域名
base-url: "http://localhost:18080" # 本地测试环境
base-url: "https://2.uzi0.cc" # 生产环境需要配置为实际域名
# base-url: "http://localhost:18080" # 本地测试环境
image-save-path: "./images" # 图片保存路径
link: