瀏覽代碼

Merge remote-tracking branch 'origin/package' into package

shishaosong 1 年之前
父節點
當前提交
ce8c6e95ec

+ 71 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/UserVisitLog.java

@@ -0,0 +1,71 @@
+package com.zanxiang.game.module.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-07-24
+ * @description : 用户访问下载地址记录
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+@Builder
+@TableName("t_user_visit_log")
+public class UserVisitLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 游戏id
+     */
+    private Long gameId;
+
+    /**
+     * 渠道id
+     */
+    private Long agentId;
+
+    /**
+     * 访问ip
+     */
+    private String ip;
+
+    /**
+     * 访问ua
+     */
+    private String ua;
+
+    /**
+     * 访问链接
+     */
+    private String url;
+
+    /**
+     * 链接参数
+     */
+    private String channel;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 12 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/mapper/UserVisitLogMapper.java

@@ -0,0 +1,12 @@
+package com.zanxiang.game.module.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zanxiang.game.module.mybatis.entity.UserVisitLog;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-07-24
+ * @description : ${description}
+ */
+public interface UserVisitLogMapper extends BaseMapper<UserVisitLog> {
+}

+ 1 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java

@@ -23,7 +23,7 @@ public class SDKApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <dubbo升级3.0, 用户渠道记录逻辑代码提交01> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <dubbo升级3.0, 提交用户访问下载地址提交记录接口> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 1 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/adapter/ArgumentAdapter.java

@@ -67,6 +67,7 @@ public class ArgumentAdapter implements HandlerMethodArgumentResolver {
         UserData data = UserData.builder()
                 .gameId(gameExt.getGameId())
                 .ip(IpUtil.getRealIp(request))
+                .ua(request.getHeader("User-Agent"))
                 .deviceType(deviceType)
                 .deviceSystem(request.getHeader("os"))
                 .mac(request.getHeader("mac"))

+ 53 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/PushController.java

@@ -0,0 +1,53 @@
+package com.zanxiang.game.module.sdk.controller;
+
+import com.zanxiang.game.module.sdk.annotation.UnSignCheck;
+import com.zanxiang.game.module.sdk.pojo.param.GameRemitLogParam;
+import com.zanxiang.game.module.sdk.pojo.param.UserVisitLogParam;
+import com.zanxiang.game.module.sdk.service.IGameRemitLogService;
+import com.zanxiang.game.module.sdk.service.IUserVisitLogService;
+import com.zanxiang.module.util.pojo.ResultVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-07-17
+ * @description : 消息推送接收
+ */
+@Api(tags = "消息推送接收接口")
+@RestController
+@RequestMapping(value = "/api")
+public class PushController {
+
+    @Autowired
+    private IGameRemitLogService gameRemitLogService;
+
+    @Autowired
+    private IUserVisitLogService userVisitLogService;
+
+    @UnSignCheck
+    @ApiOperation(value = "游戏提现记录日志推送")
+    @PostMapping("/remit/log/push")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<Boolean> addOrUpdate(@Validated @RequestBody GameRemitLogParam param) {
+        return ResultVO.ok(gameRemitLogService.addOrUpdate(param));
+    }
+
+    @UnSignCheck
+    @ApiOperation(value = "用户下载地址访问记录")
+    @PostMapping("/visit/log/push")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
+    public ResultVO<Boolean> visitLogCreate(@Validated @RequestBody UserVisitLogParam param,
+                                            @RequestHeader(name = "User-Agent") String userAgent,
+                                            HttpServletRequest httpServletRequest) {
+        return ResultVO.ok(userVisitLogService.visitLogCreate(param.getUrl(), userAgent, httpServletRequest));
+    }
+
+}

+ 0 - 38
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/controller/RemitController.java

@@ -1,38 +0,0 @@
-package com.zanxiang.game.module.sdk.controller;
-
-import com.zanxiang.game.module.sdk.annotation.UnSignCheck;
-import com.zanxiang.game.module.sdk.pojo.param.GameRemitLogParam;
-import com.zanxiang.game.module.sdk.service.IGameRemitLogService;
-import com.zanxiang.module.util.pojo.ResultVO;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * @author : lingfeng
- * @time : 2023-07-17
- * @description : 提现
- */
-@Api(tags = "注册登录接口")
-@RestController
-@RequestMapping(value = "/api/remit")
-public class RemitController {
-
-    @Autowired
-    private IGameRemitLogService gameRemitLogService;
-
-    @UnSignCheck
-    @ApiOperation(value = "游戏提现记录日志推送")
-    @PostMapping("/log/push")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "成功", response = Boolean.class)})
-    public ResultVO<Boolean> addOrUpdate(@Validated @RequestBody GameRemitLogParam param) {
-        return ResultVO.ok(gameRemitLogService.addOrUpdate(param));
-    }
-}

+ 5 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/UserData.java

@@ -35,6 +35,11 @@ public class UserData implements Serializable {
      */
     private String ip;
 
+    /**
+     * 客户端ua
+     */
+    private String ua;
+
     /**
      * token
      */

+ 22 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/param/UserVisitLogParam.java

@@ -0,0 +1,22 @@
+package com.zanxiang.game.module.sdk.pojo.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-07-24
+ * @description : 访问信息
+ */
+@Data
+public class UserVisitLogParam {
+
+    /**
+     * 访问url
+     */
+    @NotBlank(message = "访问url不可为空")
+    @ApiModelProperty(notes = "访问url")
+    private String url;
+}

+ 24 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/IUserVisitLogService.java

@@ -0,0 +1,24 @@
+package com.zanxiang.game.module.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.game.module.mybatis.entity.UserVisitLog;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-07-24
+ * @description : 用户下载地址访问记录
+ */
+public interface IUserVisitLogService extends IService<UserVisitLog> {
+
+    /**
+     * 访问日志创建
+     *
+     * @param url                url
+     * @param userAgent          用户代理
+     * @param httpServletRequest http servlet请求
+     * @return boolean
+     */
+    boolean visitLogCreate(String url, String userAgent, HttpServletRequest httpServletRequest);
+}

+ 138 - 7
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/AgentServiceImpl.java

@@ -5,11 +5,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zanxiang.game.module.mybatis.entity.Agent;
-import com.zanxiang.game.module.mybatis.entity.User;
-import com.zanxiang.game.module.mybatis.entity.UserLoginLog;
+import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
+import com.zanxiang.game.module.mybatis.entity.*;
 import com.zanxiang.game.module.mybatis.mapper.AgentMapper;
+import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.service.*;
+import com.zanxiang.module.util.DateUtil;
 import com.zanxiang.module.util.JsonUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.logging.log4j.util.Strings;
@@ -19,10 +20,7 @@ import reactor.util.function.Tuple3;
 import reactor.util.function.Tuples;
 
 import java.time.LocalDateTime;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @author bilingfeng
@@ -43,6 +41,15 @@ public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements
     @Autowired
     private IUserAgentLogService userAgentLogService;
 
+    @Autowired
+    private IUserVisitLogService userVisitLogService;
+
+    @Autowired
+    private IGameService gameService;
+
+    @Autowired
+    private IGameExtService gameExtService;
+
     @Override
     public void userAgentUpdate(User user, String channel) {
         log.error("用户登录接收到的渠道参数 userId : {}, channel : {}", user.getId(), channel);
@@ -112,6 +119,130 @@ public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements
         return Tuples.of(agent.getId(), urlParamMap, Strings.isBlank(shareUserId) ? Strings.EMPTY : shareUserId);
     }
 
+    public Tuple3<Long, Map<String, String>, String> getUserAgentId(UserData userData) {
+        GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
+        Game game = gameService.getById(userData.getGameId());
+        String channel = userData.getChannel();
+
+        if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_APP.getId())
+                && !Objects.equals(gameExt.getAdCallBackSwitch(), Boolean.TRUE)){
+
+        }
+
+
+
+        log.error("用户注册接收到的渠道参数 channel : {}", channel);
+        if (Strings.isBlank(channel)) {
+            return Tuples.of(0L, Collections.emptyMap(), Strings.EMPTY);
+        }
+        //数据解析
+        Map<String, String> urlParamMap = this.channelTransform(channel);
+        String shareUserId = urlParamMap.get("shareUserId");
+        //查询渠道
+        Agent agent = null;
+        if (Strings.isNotBlank(shareUserId)) {
+            User user = userService.getById(Long.valueOf(shareUserId));
+            if (user != null) {
+                agent = super.getById(user.getAgentId());
+            }
+        } else {
+            agent = this.getAgentByKey(urlParamMap);
+        }
+        if (agent == null) {
+
+
+
+
+
+
+            return Tuples.of(0L, Collections.emptyMap(), Strings.isBlank(shareUserId) ? Strings.EMPTY : shareUserId);
+        }
+        return Tuples.of(agent.getId(), urlParamMap, Strings.isBlank(shareUserId) ? Strings.EMPTY : shareUserId);
+    }
+
+    public Long getUserAgentIda(UserData userData) {
+        //当前时间
+        LocalDateTime regTime = LocalDateTime.now();
+        // 0. 优先前置策略, 单独判断ip是否只有一个用户, 只有一个用户则是该用户
+        List<UserVisitLog> userVisitLogList = userVisitLogService.list(new LambdaQueryWrapper<UserVisitLog>()
+                .eq(UserVisitLog::getIp, userData.getIp()));
+        if (CollectionUtils.isNotEmpty(userVisitLogList) && userVisitLogList.size() == 1) {
+            return userVisitLogList.get(0).getAgentId();
+        }
+        // 1. 策略一 : 根据 ip + ua 精准匹配用户
+        userVisitLogList = userVisitLogService.list(new LambdaQueryWrapper<UserVisitLog>()
+                .eq(UserVisitLog::getIp, userData.getIp())
+                .eq(UserVisitLog::getUa, userData)
+                .le(UserVisitLog::getCreateTime, regTime));
+        // 判断通过策略一是否拿到数据, 筛选出访问时间与注册时间最接近的
+        if (CollectionUtils.isNotEmpty(userVisitLogList)) {
+            UserVisitLog lastUserCall = this.getLastUserCall(userVisitLogList, regTime);
+            return lastUserCall == null ? null : lastUserCall.getAgentId();
+        }
+        // 2. 策略二 : 根据 ip + regTime 模糊匹配用户, 数据误差在15分钟以内
+        userVisitLogList = userVisitLogService.list(new LambdaQueryWrapper<UserVisitLog>()
+                .eq(UserVisitLog::getIp, userData.getIp())
+                .ge(UserVisitLog::getCreateTime, regTime.minusMinutes(15))
+                .le(UserVisitLog::getCreateTime, regTime));
+        // 判断通过策略二是否拿到数据, 筛选出访问时间与注册时间最接近的
+        if (CollectionUtils.isNotEmpty(userVisitLogList)) {
+            UserVisitLog lastUserCall = this.getLastUserCall(userVisitLogList, regTime);
+            return lastUserCall == null ? null : lastUserCall.getAgentId();
+        }
+        // 3. 策略三 : 根据 ua + regTime 模糊匹配用户, 数据误差在15分钟以内
+        userVisitLogList = userVisitLogService.list(new LambdaQueryWrapper<UserVisitLog>()
+                .eq(UserVisitLog::getUa, userData.getUa())
+                .ge(UserVisitLog::getCreateTime, regTime.minusMinutes(15))
+                .le(UserVisitLog::getCreateTime, regTime));
+        // 判断通过策略三是否拿到数据, 筛选出访问时间与注册时间最接近的
+        if (CollectionUtils.isNotEmpty(userVisitLogList)) {
+            UserVisitLog lastUserCall = this.getLastUserCall(userVisitLogList, regTime);
+            return lastUserCall == null ? null : lastUserCall.getAgentId();
+        }
+        // 4. 策略四 : 根据 regTime 模糊匹配用户, 数据误差在15分钟以内, 此策略为兜底策略
+        userVisitLogList = userVisitLogService.list(new LambdaQueryWrapper<UserVisitLog>()
+                .ge(UserVisitLog::getCreateTime, regTime.minusMinutes(15))
+                .le(UserVisitLog::getCreateTime, regTime));
+        if (CollectionUtils.isNotEmpty(userVisitLogList)) {
+            UserVisitLog lastUserCall = this.getLastUserCall(userVisitLogList, regTime);
+            return lastUserCall == null ? null : lastUserCall.getAgentId();
+        }
+        return null;
+    }
+
+    /**
+     * 对比获取访问时间与注册时间最接近的访问记录
+     *
+     * @param regTime          用户注册时间
+     * @param userVisitLogList 用户访问日志列表
+     * @return {@link UserVisitLog}
+     */
+    private UserVisitLog getLastUserCall(List<UserVisitLog> userVisitLogList, LocalDateTime regTime) {
+        //只匹配到一个用户, 直接返回
+        if (userVisitLogList.size() == 1) {
+            return userVisitLogList.get(0);
+        }
+        //注册时间转换毫秒
+        long regTimeMill = DateUtil.localDateTimeToMilli(regTime);
+        //用户访问时间与注册时间差
+        Map<Long, Long> timeDiffMap = new HashMap<>(userVisitLogList.size());
+        //循环用户列表
+        userVisitLogList.forEach(user -> {
+            //用户访问时间
+            LocalDateTime createTime = user.getCreateTime();
+            //用户访问时间转换
+            long createTimeMill = DateUtil.localDateTimeToMilli(createTime);
+            //计算时间差, 并添加到集合
+            timeDiffMap.put(user.getId(), regTimeMill - createTimeMill);
+        });
+        //取出map中最小的value值对应的key值
+        List<Map.Entry<Long, Long>> list = new ArrayList<>(timeDiffMap.entrySet());
+        list.sort(Comparator.comparingLong(Map.Entry::getValue));
+        Long minId = list.get(0).getKey();
+        //过滤出对应的对象返回
+        return userVisitLogList.stream().filter(user -> Objects.equals(user.getId(), minId)).findFirst().orElse(null);
+    }
+
     private Agent getAgentByKey(Map<String, String> urlParamMap) {
         //参数不存在
         if (CollectionUtils.isEmpty(urlParamMap)) {

+ 95 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/UserVisitLogServiceImpl.java

@@ -0,0 +1,95 @@
+package com.zanxiang.game.module.sdk.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.game.module.mybatis.entity.Agent;
+import com.zanxiang.game.module.mybatis.entity.UserVisitLog;
+import com.zanxiang.game.module.mybatis.mapper.UserVisitLogMapper;
+import com.zanxiang.game.module.sdk.service.IAgentService;
+import com.zanxiang.game.module.sdk.service.IUserVisitLogService;
+import com.zanxiang.module.web.util.IpUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.logging.log4j.util.Strings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.URLDecoder;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-07-24
+ * @description : 用户下载地址访问记录
+ */
+@Slf4j
+@Service
+public class UserVisitLogServiceImpl extends ServiceImpl<UserVisitLogMapper, UserVisitLog> implements IUserVisitLogService {
+
+    @Autowired
+    private IAgentService agentService;
+
+    @Override
+    public boolean visitLogCreate(String url, String userAgent, HttpServletRequest httpServletRequest) {
+        //解析url
+        Map<String, String> urlParamMap = this.getUrlParameter(url);
+        //获取渠道标识
+        String state = urlParamMap.get("agentId");
+        if (Strings.isBlank(state)) {
+            state = urlParamMap.get("agentKey");
+        }
+        //查询渠道信息
+        Agent agent = agentService.getOne(new LambdaQueryWrapper<Agent>().eq(Agent::getAgentKey, state));
+        //保存访问记录
+        return super.save(UserVisitLog.builder()
+                .gameId(agent == null ? null : agent.getGameId())
+                .gameId(agent == null ? null : agent.getId())
+                .ip(IpUtil.getRealIp(httpServletRequest))
+                .ua(userAgent)
+                .url(urlParamMap.get("url"))
+                .channel(urlParamMap.get("channel"))
+                .createTime(LocalDateTime.now())
+                .updateTime(LocalDateTime.now())
+                .build());
+    }
+
+    /**
+     * 解析头条的url的host和参数
+     *
+     * @param url : 请求的url
+     * @return : 返回url中携带的参数的和host地址
+     */
+    private Map<String, String> getUrlParameter(String url) {
+        //参数判断
+        if (Strings.isBlank(url)) {
+            return Collections.emptyMap();
+        }
+        //没有拼接参数
+        if (url.indexOf('?') == -1) {
+            return Collections.singletonMap("url", url);
+        }
+        //参数map
+        Map<String, String> map = new HashMap<>(5);
+        try {
+            final String charset = "utf-8";
+            url = URLDecoder.decode(url, charset);
+            //添加url的host
+            map.put("url", url.substring(0, url.indexOf('?')));
+            map.put("channel", url.substring(url.indexOf('?')));
+            //解析参数
+            final String contents = url.substring(url.indexOf('?') + 1);
+            String[] keyValues = contents.split("&");
+            for (String keyValue : keyValues) {
+                String key = keyValue.substring(0, keyValue.indexOf("="));
+                String value = keyValue.substring(keyValue.indexOf("=") + 1);
+                map.put(key, value);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return map;
+    }
+}