|
|
@@ -1,4 +1,75 @@
|
|
1
|
1
|
package com.ruoyi.novel.controller;
|
|
2
|
2
|
//# 广告计数接口
|
|
|
3
|
+
|
|
|
4
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
5
|
+
|
|
|
6
|
+@RestController
|
|
|
7
|
+@RequestMapping("/ad")
|
|
3
|
8
|
public class NovelAdController {
|
|
4
|
|
-}
|
|
|
9
|
+
|
|
|
10
|
+ private final AdService adService;
|
|
|
11
|
+ private final RedisTemplate<String, String> redisTemplate;
|
|
|
12
|
+
|
|
|
13
|
+ @Autowired
|
|
|
14
|
+ public NovelAdController(AdService adService, RedisTemplate<String, String> redisTemplate) {
|
|
|
15
|
+ this.adService = adService;
|
|
|
16
|
+ this.redisTemplate = redisTemplate;
|
|
|
17
|
+ }
|
|
|
18
|
+
|
|
|
19
|
+ @PostMapping("/count")
|
|
|
20
|
+ public ResponseEntity<?> countAdView(
|
|
|
21
|
+ @RequestBody AdCountRequest request,
|
|
|
22
|
+ @RequestHeader(value = "X-PHP-Session", required = false) String phpSessionId
|
|
|
23
|
+ ) {
|
|
|
24
|
+ // 验证请求来源
|
|
|
25
|
+ if (!isValidSource(request.getSource())) {
|
|
|
26
|
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid source");
|
|
|
27
|
+ }
|
|
|
28
|
+
|
|
|
29
|
+ // 会话同步处理
|
|
|
30
|
+ String userId = resolveUserId(request, phpSessionId);
|
|
|
31
|
+
|
|
|
32
|
+ // 防重处理 (5分钟内同一章节只计数一次)
|
|
|
33
|
+ String redisKey = String.format("ad:%s:%s:%s",
|
|
|
34
|
+ request.getSource(), userId, request.getChapterId());
|
|
|
35
|
+
|
|
|
36
|
+ if (redisTemplate.hasKey(redisKey)) {
|
|
|
37
|
+ return ResponseEntity.ok("Already counted");
|
|
|
38
|
+ }
|
|
|
39
|
+
|
|
|
40
|
+ // 记录广告
|
|
|
41
|
+ adService.logAdView(
|
|
|
42
|
+ userId,
|
|
|
43
|
+ request.getChapterId(),
|
|
|
44
|
+ request.getAdType(),
|
|
|
45
|
+ request.getSource()
|
|
|
46
|
+ );
|
|
|
47
|
+
|
|
|
48
|
+ // 设置Redis锁
|
|
|
49
|
+ redisTemplate.opsForValue().set(redisKey, "1", Duration.ofMinutes(5));
|
|
|
50
|
+
|
|
|
51
|
+ return ResponseEntity.ok("Ad counted");
|
|
|
52
|
+ }
|
|
|
53
|
+
|
|
|
54
|
+ private String resolveUserId(AdCountRequest request, String phpSessionId) {
|
|
|
55
|
+ // 优先使用PHP会话
|
|
|
56
|
+ if (phpSessionId != null) {
|
|
|
57
|
+ String sessionKey = "session:" + phpSessionId;
|
|
|
58
|
+ String userId = redisTemplate.opsForValue().get(sessionKey + ":user_id");
|
|
|
59
|
+ if (userId != null) return userId;
|
|
|
60
|
+ }
|
|
|
61
|
+
|
|
|
62
|
+ // 其次使用请求中的用户ID
|
|
|
63
|
+ if (request.getUserId() != null) {
|
|
|
64
|
+ return request.getUserId();
|
|
|
65
|
+ }
|
|
|
66
|
+
|
|
|
67
|
+ // 最后使用设备ID
|
|
|
68
|
+ return request.getDeviceId();
|
|
|
69
|
+ }
|
|
|
70
|
+
|
|
|
71
|
+ private boolean isValidSource(String source) {
|
|
|
72
|
+ List<String> validSources = Arrays.asList("h5", "wechat", "douyin", "app");
|
|
|
73
|
+ return validSources.contains(source);
|
|
|
74
|
+ }
|
|
|
75
|
+}
|