From 6dcbfa025a0717323f681a548c8e728e860fbff5 Mon Sep 17 00:00:00 2001 From: xupei Date: Wed, 13 May 2026 16:26:47 +0800 Subject: [PATCH] =?UTF-8?q?TASK:Global=20Award=E8=AE=B0=E5=BD=95=E8=AE=BF?= =?UTF-8?q?=E5=AE=A2=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../da/controller/GlobalAwardController.java | 14 ++++++++ .../com/ai/da/model/vo/PageVisitCountVO.java | 23 +++++++++++++ .../com/ai/da/service/GlobalAwardService.java | 17 ++++++++++ .../service/impl/GlobalAwardServiceImpl.java | 32 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 src/main/java/com/ai/da/model/vo/PageVisitCountVO.java diff --git a/src/main/java/com/ai/da/controller/GlobalAwardController.java b/src/main/java/com/ai/da/controller/GlobalAwardController.java index a63d566b..f8952053 100644 --- a/src/main/java/com/ai/da/controller/GlobalAwardController.java +++ b/src/main/java/com/ai/da/controller/GlobalAwardController.java @@ -5,6 +5,7 @@ import com.ai.da.model.dto.*; import com.ai.da.model.dto.ContestantDTO; import com.ai.da.model.vo.CheckOTPVO; import com.ai.da.model.vo.ContestantCountVO; +import com.ai.da.model.vo.PageVisitCountVO; import com.ai.da.service.GlobalAwardService; import com.ai.da.service.upload.UploadService; import com.ai.da.service.upload.UploadTask; @@ -198,6 +199,19 @@ public class GlobalAwardController { return Response.success(globalAwardService.getContestantCount()); } + @PostMapping("/page/visit") + @ApiOperation(value = "记录比赛页面访问量", notes = "记录比赛页面的访问量,包含两种统计方式:每次访问/刷新计一次,以及5秒内刷新只计一次") + public Response recordPageVisit(@ApiParam(value = "会话ID,用于5秒内去重判断", required = false) @RequestParam(value = "sessionId", required = false) String sessionId) { + globalAwardService.recordPageVisit(sessionId); + return Response.success(); + } + + @GetMapping("/page/visit/count") + @ApiOperation(value = "获取比赛页面访问量", notes = "获取比赛页面的两种访问量:rawVisitCount(每次访问/刷新计一次)和 uniqueVisitCount(5秒内刷新只计一次)") + public Response getPageVisitCount() { + return Response.success(globalAwardService.getPageVisitCount()); + } + } diff --git a/src/main/java/com/ai/da/model/vo/PageVisitCountVO.java b/src/main/java/com/ai/da/model/vo/PageVisitCountVO.java new file mode 100644 index 00000000..ff8bf103 --- /dev/null +++ b/src/main/java/com/ai/da/model/vo/PageVisitCountVO.java @@ -0,0 +1,23 @@ +package com.ai.da.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageVisitCountVO { + + /** + * 每次访问或刷新都计一次(不去重) + */ + private Long rawVisitCount; + + /** + * 5秒内刷新只算一次(去重后的真实访客数) + */ + private Long uniqueVisitCount; +} diff --git a/src/main/java/com/ai/da/service/GlobalAwardService.java b/src/main/java/com/ai/da/service/GlobalAwardService.java index 76232238..d892e8c0 100644 --- a/src/main/java/com/ai/da/service/GlobalAwardService.java +++ b/src/main/java/com/ai/da/service/GlobalAwardService.java @@ -3,6 +3,7 @@ package com.ai.da.service; import com.ai.da.model.dto.ContestantDTO; import com.ai.da.model.vo.CheckOTPVO; import com.ai.da.model.vo.ContestantCountVO; +import com.ai.da.model.vo.PageVisitCountVO; import org.springframework.web.multipart.MultipartFile; import java.util.Map; @@ -46,6 +47,22 @@ public interface GlobalAwardService { * @return 参赛者数量和最大参赛者编号 */ ContestantCountVO getContestantCount(); + + /** + * 记录比赛页面的访问量 + *
    + *
  • rawVisitCount: 每次访问或刷新都计一次(不去重)
  • + *
  • uniqueVisitCount: 5秒内刷新只算一次(基于会话去重)
  • + *
+ * @param sessionId 会话ID(用于5秒去重判断) + */ + void recordPageVisit(String sessionId); + + /** + * 获取比赛页面的两种访问量 + * @return 原始访问量和去重访问量 + */ + PageVisitCountVO getPageVisitCount(); } diff --git a/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java b/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java index 804c10cd..53e7812c 100644 --- a/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GlobalAwardServiceImpl.java @@ -13,6 +13,7 @@ import com.ai.da.model.dto.ContestantDTO; import com.ai.da.model.dto.PublishSysNotificationDTO; import com.ai.da.model.vo.CheckOTPVO; import com.ai.da.model.vo.ContestantCountVO; +import com.ai.da.model.vo.PageVisitCountVO; import com.ai.da.service.GlobalAwardService; import com.ai.da.service.MessageCenterService; import com.alibaba.fastjson.JSON; @@ -619,6 +620,37 @@ public class GlobalAwardServiceImpl implements GlobalAwardService { private String nullSafe(String value) { return value != null ? value : "N/A"; } + + private static final String RAW_VISIT_COUNT_KEY = "GLOBAL_AWARD:visit:raw"; + private static final String UNIQUE_VISIT_SET_KEY = "GLOBAL_AWARD:visit:unique"; + private static final String SESSION_VISIT_KEY_PREFIX = "GLOBAL_AWARD:visit:session:"; + private static final long SESSION_DEDUP_SECONDS = 5L; + + @Override + public void recordPageVisit(String sessionId) { + redisUtil.increaseCount(RAW_VISIT_COUNT_KEY); + + if (StringUtils.isNotBlank(sessionId)) { + String sessionKey = SESSION_VISIT_KEY_PREFIX + sessionId; + if (!redisUtil.hasKey(sessionKey)) { + redisUtil.increaseCount(UNIQUE_VISIT_SET_KEY); + redisUtil.addToString(sessionKey, "1", SESSION_DEDUP_SECONDS); + } + } else { + redisUtil.increaseCount(UNIQUE_VISIT_SET_KEY); + } + } + + @Override + public PageVisitCountVO getPageVisitCount() { + Long raw = redisUtil.getIncrementCount(RAW_VISIT_COUNT_KEY); + Long unique = redisUtil.getIncrementCount(UNIQUE_VISIT_SET_KEY); + return PageVisitCountVO.builder() + .rawVisitCount(raw != null ? raw : 0L) + .uniqueVisitCount(unique != null ? unique : 0L) + .build(); + } + }