本文章是基于我的另一篇博客所写的相关代码,如果还没看过的可以先看看我这篇文章:

https://blog.csdn.net/qq_56769991/article/details/123915587

核心代码

自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisitLogger {/*** 访问行为枚举*/VisitBehavior value() default VisitBehavior.UNKNOWN;
}

切面配置:

@Component
@Aspect  //该类用作切面类
public class VisitLogAspect {@AutowiredVisitLogService visitLogService;@AutowiredVisitorService visitorService;@AutowiredRedisService redisService;ThreadLocal<Long> currentTime = new ThreadLocal<>();/*** 配置切入点:表示被VisitLogger注解的方法都可以设置为切入点。* 下面的没有内容的方法,其实就是代表被视为切入点的那个方法,在形参中我们传入了参数visitLogger,因为我们要使用被* 切入点方法注解中的相关参数*/@Pointcut("@annotation(visitLogger)")public void logPointcut(VisitLogger visitLogger) {}/*** 配置环绕通知** @param joinPoint* @return* @throws Throwable* 在切入点的前后进行操作* 这里的ProceedingJoinPoint表示被切入点的那个方法里面的逻辑内容* visitLogger表示我们注解的内容* 这些参数系统会帮我们自动传入* 在环绕通知的返回值应该与被环绕的方法的返回值一致*/@Around("logPointcut(visitLogger)")public Object logAround(ProceedingJoinPoint joinPoint, VisitLogger visitLogger) throws Throwable {currentTime.set(System.currentTimeMillis());Result result = (Result) joinPoint.proceed();int times = (int) (System.currentTimeMillis() - currentTime.get());currentTime.remove();//获取请求对象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//校验访客标识码String identification = checkIdentification(request);//记录访问日志VisitLog visitLog = handleLog(joinPoint, visitLogger, request, result, times, identification);//存到数据库visitLogService.saveVisitLog(visitLog);return result;}/*** 校验访客标识码** @param request* @return*/private String checkIdentification(HttpServletRequest request) {String identification = request.getHeader("identification");if (identification == null) {//请求头没有uuid,签发uuid并保存到数据库和Redisidentification = saveUUID(request);} else {//校验Redis中是否存在uuidboolean redisHas = redisService.hasValueInSet(RedisKeyConstants.IDENTIFICATION_SET, identification);//Redis中不存在uuidif (!redisHas) {//校验数据库中是否存在uuidboolean mysqlHas = visitorService.hasUUID(identification);if (mysqlHas) {//数据库存在,保存至RedisredisService.saveValueToSet(RedisKeyConstants.IDENTIFICATION_SET, identification);} else {//数据库不存在,签发新的uuididentification = saveUUID(request);}}}return identification;}/*** 签发UUID,并保存至数据库和Redis** @param request* @return*/private String saveUUID(HttpServletRequest request) {//获取响应对象HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();//获取当前时间戳,精确到小时,防刷访客数据Calendar calendar = Calendar.getInstance();calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);String timestamp = Long.toString(calendar.getTimeInMillis() / 1000);//获取访问者基本信息String ip = IpAddressUtils.getIpAddress(request);String userAgent = request.getHeader("User-Agent");//根据时间戳、ip、userAgent生成UUIDString nameUUID = timestamp + ip + userAgent;String uuid = UUID.nameUUIDFromBytes(nameUUID.getBytes()).toString();//添加访客标识码UUID至响应头response.addHeader("identification", uuid);//暴露自定义header供页面资源使用response.addHeader("Access-Control-Expose-Headers", "identification");//校验Redis中是否存在uuidboolean redisHas = redisService.hasValueInSet(RedisKeyConstants.IDENTIFICATION_SET, uuid);if (!redisHas) {//保存至RedisredisService.saveValueToSet(RedisKeyConstants.IDENTIFICATION_SET, uuid);//保存至数据库Visitor visitor = new Visitor(uuid, ip, userAgent);visitorService.saveVisitor(visitor);}return uuid;}/*** 设置VisitLogger对象属性** @param joinPoint* @param visitLogger* @param result* @param times* @return*/private VisitLog handleLog(ProceedingJoinPoint joinPoint, VisitLogger visitLogger, HttpServletRequest request, Result result,int times, String identification) {String uri = request.getRequestURI();String method = request.getMethod();  //获取请求方法String ip = IpAddressUtils.getIpAddress(request);String userAgent = request.getHeader("User-Agent");//通过切面的ProceedingJoinPoint获取相关信息的工具类,在这里主要是获得请求参数Map<String, Object> requestParams = AopUtils.getRequestParams(joinPoint);//这里visitLogRemark是visitlog表中的一小部分VisitLogRemark visitLogRemark = judgeBehavior(visitLogger.value(), requestParams, result);VisitLog log = new VisitLog(identification, uri, method, visitLogger.value().getBehavior(),visitLogRemark.getContent(), visitLogRemark.getRemark(), ip, times, userAgent);log.setParam(StringUtils.substring(JacksonUtils.writeValueAsString(requestParams), 0, 2000));
//      将visitLog相关信息查询到后放到对象中并返回return log;}/*** 根据访问行为,设置对应的访问内容或备注** @param behavior* @param requestParams* @param result* @return*/private VisitLogRemark judgeBehavior(VisitBehavior behavior, Map<String, Object> requestParams, Result result) {String remark = "";String content = behavior.getContent();switch (behavior) {case INDEX:case MOMENT:remark = "第" + requestParams.get("pageNum") + "页";break;case BLOG:if (result.getCode() == 200) {BlogDetail blog = (BlogDetail) result.getData();String title = blog.getTitle();content = title;remark = "文章标题:" + title;}break;case SEARCH:if (result.getCode() == 200) {String query = (String) requestParams.get("query");content = query;remark = "搜索内容:" + query;}break;case CATEGORY:String categoryName = (String) requestParams.get("categoryName");content = categoryName;remark = "分类名称:" + categoryName + ",第" + requestParams.get("pageNum") + "页";break;case TAG:String tagName = (String) requestParams.get("tagName");content = tagName;remark = "标签名称:" + tagName + ",第" + requestParams.get("pageNum") + "页";break;case CLICK_FRIEND:String nickname = (String) requestParams.get("nickname");content = nickname;remark = "友链名称:" + nickname;break;}return new VisitLogRemark(content, remark);}
}

工具类:

@Slf4j
@Component
public class IpAddressUtils {/*** 在Nginx等代理之后获取用户真实IP地址** @param request* @return*/public static String getIpAddress(HttpServletRequest request) {String ip = request.getHeader("X-Real-IP");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("x-forwarded-for");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {//根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();} catch (UnknownHostException e) {log.error("getIpAddress exception:", e);}ip = inet.getHostAddress();}}return StringUtils.substringBefore(ip, ",");}private static DbSearcher searcher;private static Method method;/*** 在服务启动时加载 ip2region.db 到内存中* 解决打包jar后找不到 ip2region.db 的问题** @throws Exception 出现异常应该直接抛出终止程序启动,避免后续invoke时出现更多错误*/@PostConstructprivate void initIp2regionResource() throws Exception {InputStream inputStream = new ClassPathResource("/ipdb/ip2region.db").getInputStream();//将 ip2region.db 转为 ByteArraybyte[] dbBinStr = FileCopyUtils.copyToByteArray(inputStream);DbConfig dbConfig = new DbConfig();searcher = new DbSearcher(dbConfig, dbBinStr);//二进制方式初始化 DBSearcher,需要使用基于内存的查找算法 memorySearchmethod = searcher.getClass().getMethod("memorySearch", String.class);}/*** 根据ip从 ip2region.db 中获取地理位置** @param ip* @return*/public static String getCityInfo(String ip) {if (ip == null || !Util.isIpAddress(ip)) {log.error("Error: Invalid ip address");return "";}try {DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);String ipInfo = dataBlock.getRegion();if (!StringUtils.isEmpty(ipInfo)) {ipInfo = ipInfo.replace("|0", "");ipInfo = ipInfo.replace("0|", "");return ipInfo;}} catch (Exception e) {log.error("getCityInfo exception:", e);}return "";}
}
@Component
public class UserAgentUtils {private UserAgentAnalyzer uaa;public UserAgentUtils() {this.uaa = UserAgentAnalyzer.newBuilder().hideMatcherLoadStats().withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION_MAJOR).withField(UserAgent.AGENT_NAME_VERSION).build();}/*** 从User-Agent解析客户端操作系统和浏览器版本** @param userAgent* @return*/public Map<String, String> parseOsAndBrowser(String userAgent) {UserAgent agent = uaa.parse(userAgent);String os = agent.getValue(UserAgent.OPERATING_SYSTEM_NAME_VERSION_MAJOR);String browser = agent.getValue(UserAgent.AGENT_NAME_VERSION);Map<String, String> map = new HashMap<>();map.put("os", os);map.put("browser", browser);return map;}
}
public class AopUtils {private static Set<String> ignoreParams = new HashSet<String>() {{add("jwt");}};/*** 获取请求参数** @param joinPoint* @return*/public static Map<String, Object> getRequestParams(JoinPoint joinPoint) {Map<String, Object> map = new LinkedHashMap<>();String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); //获得切入点方法的参数的名字Object[] args = joinPoint.getArgs();  //获得参数对应的值for (int i = 0; i < args.length; i++) {if (!isIgnoreParams(parameterNames[i]) && !isFilterObject(args[i])) {map.put(parameterNames[i], args[i]);}}return map;}/*** consider if the data is file, httpRequest or response** @param o the data* @return if match return true, else return false*/private static boolean isFilterObject(final Object o) {return o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof MultipartFile;}/*** 判断是否忽略参数** @param params* @return*/private static boolean isIgnoreParams(String params) {return ignoreParams.contains(params);}
}

数据库

visitor

DROP TABLE IF EXISTS `visitor`;
CREATE TABLE `visitor`  (`id` bigint(0) NOT NULL AUTO_INCREMENT,`uuid` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '访客标识码',`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip',`ip_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip来源',`os` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作系统',`browser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '浏览器',`create_time` datetime(0) NOT NULL COMMENT '首次访问时间',`last_time` datetime(0) NOT NULL COMMENT '最后访问时间',`pv` int(0) NULL DEFAULT NULL COMMENT '访问页数统计',`user_agent` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'user-agent用户代理',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `idx_uuid`(`uuid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

visit_log

DROP TABLE IF EXISTS `visit_log`;
CREATE TABLE `visit_log`  (`id` bigint(0) NOT NULL AUTO_INCREMENT,`uuid` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '访客标识码',`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求接口',`method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求方式',`param` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求参数',`behavior` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '访问行为',`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '访问内容',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip',`ip_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip来源',`os` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作系统',`browser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '浏览器',`times` int(0) NOT NULL COMMENT '请求耗时(毫秒)',`create_time` datetime(0) NOT NULL COMMENT '访问时间',`user_agent` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'user-agent用户代理',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

result

@NoArgsConstructor
@Getter
@Setter
@ToString
public class Result {private Integer code;private String msg;private Object data;private Result(Integer code, String msg) {this.code = code;this.msg = msg;this.data = null;}private Result(Integer code, String msg, Object data) {this.code = code;this.msg = msg;this.data = data;}public static Result ok(String msg, Object data) {return new Result(200, msg, data);}public static Result ok(String msg) {return new Result(200, msg);}public static Result error(String msg) {return new Result(500, msg);}public static Result error() {return new Result(500, "异常错误");}public static Result create(Integer code, String msg, Object data) {return new Result(code, msg, data);}public static Result create(Integer code, String msg) {return new Result(code, msg);}
}
public enum VisitBehavior {UNKNOWN("UNKNOWN", "UNKNOWN"),INDEX("访问页面", "首页"),ARCHIVE("访问页面", "归档"),MOMENT("访问页面", "动态"),FRIEND("访问页面", "友链"),ABOUT("访问页面", "关于我"),BLOG("查看博客", ""),CATEGORY("查看分类", ""),TAG("查看标签", ""),SEARCH("搜索博客", ""),CLICK_FRIEND("点击友链", ""),LIKE_MOMENT("点赞动态", ""),CHECK_PASSWORD("校验博客密码", ""),;/*** 访问行为*/private String behavior;/*** 访问内容*/private String content;VisitBehavior(String behavior, String content) {this.behavior = behavior;this.content = content;}public String getBehavior() {return behavior;}public String getContent() {return content;}
}

Controller

@VisitLogger(VisitBehavior.INDEX)@GetMapping("/blogs")public Result blogs(@RequestParam(defaultValue = "1") Integer pageNum) {PageResult<BlogInfo> pageResult = blogService.getBlogInfoListByIsPublished(pageNum);return Result.ok("请求成功", pageResult);}

关于service层的代码就不给出了,自己进行实现就行

基于博客系统的访客日志记录----代码合集相关推荐

  1. 基于博客系统的访客日志记录

    当我们做的一些应用需要记录一些接口被访问时用户的信息时,我们就需要用到一些记录请求的技术,并记录日志到数据库.本文章使用的方法:注解+AOP 原理:事先在数据库中建立一个记录访客日志的一张表.先自定义 ...

  2. 基于SSM开发的校园访客登记系统 JAVA

    10044基于SSM开发的校园访客登记系统 技术 Spring + SpringMVC + Mybatis 工具 eclipse + tomact + mysql + jdk 功能详情 前台功能 后台 ...

  3. 基于.NET 6 的开源访客管理系统

    简单介绍一下系统功能 系统用于简化访客登记.查询.保存.传统的登记方式,不仅浪费纸张,而且还面临保存的问题,查阅不方便. 该系统为了在疫情期间能很好管理访客登记做好风险管控,同时可以整合智能设备做到自 ...

  4. 访客管理系统 - 天天访客

    2020年初的一场突如其来的新冠疫情让全国各行各业变得措手不及.不论是疫情防控阶段,还是在复工复产阶段,人与人之间保持合理距离是切断病毒传波的根本方法.在后疫情时代,企业复工复产而产生的人员流动就对访 ...

  5. PHP之自定义阿里云客服在线访客名片

    引言 在做电商模块开发的同学总会遇到这么一个需求,那就是售前售后的咨询,也就是所谓的客服功能,想要自己从零开发一个客服系统那也不是不可能,只是工程比较大,开发周期会就一些.如果想要快速上线的话,那就得 ...

  6. 自助访客登记与访客身份核验立式一体机

    访客系统基本功能: 1.智能预约:可通过现场登记.网页提交信息.微信公众号预约等多种方式进行访客登记,可与企业OA系统对接,系统化管理,操作便捷. 2.证件+人脸身份验证:访客身份证.RFID访客卡. ...

  7. 抖音获客源码,短视频获客系统,获客SaaS有标准答案吗?

    抖音获客源码,短视频获客系统,获客,这一SaaS行业的垂直细分领域,有没有可能,存在一种标准答案? 带着疑问打开浏览器,在某度中,我们尝试输入"抖音获客源码"."短视频获 ...

  8. 私人定制外贸精准获客产品 外贸智能获客系统 好选客

    当下疫情奥密克戎全球施虐,严重影响外贸企业正常的全球采购商的开发,传统的外贸开发客户方式全球国际展会已经静止三年有余,目前疫情情况下还有很远的路要走.B2B国际网站模式过于传统导致同行竞品竞争激烈已毫 ...

  9. qt 历史记录控件_基于Qt图形界面软件的操作日志记录方法及系统_2015106293015_说明书_专利查询_专利网_钻瓜专利网...

    技术领域 本发明涉及一种软件系统的日志记录技术,特别涉及一种基于Qt图形界面软件的操作日志记录方法及系统. 背景技术 软件操作日志是记录用户在使用软件的过程中,通过鼠标和键盘在操作界面上执行的点击和输 ...

最新文章

  1. ActiveMQ 学习(VM Transport)
  2. 海量数据处理之倒排索引
  3. c#中textbox属性_C#.Net中带有示例的TextBox.Multiline属性
  4. header()函数使用说明
  5. Android 中View仅仅能接收到ACTION_DOWN无法接收ACTION_MOVE和ACTION_UP解决的方法
  6. Docker部署Nebula Graph2.0和Studio
  7. Mysql 的ANY_VALUE()函数和 ONLY_FULL_GROUP_BY 模式
  8. Java中submit的方法,线程池中 submit()和 execute()方法区别
  9. [Python数据分析]NBA的球星们喜欢在哪个位置出手
  10. 无法删除IE图标(被劫持)
  11. 正是岳麓好风景,软件逢君正当时
  12. CSS优先级权重练习
  13. python和易语言哪个容易胖_碳水化合物和脂肪哪个更容易让身体发胖?
  14. 我的开源项目,趣享GIF源代码已正式公开
  15. elementplus 上传文件
  16. Rainbow 开发 step1
  17. Backtrader官方中文文档
  18. 深圳软件测试培训:移动测试ExpandableListView
  19. 家庭关系代码 GB/T 4761-2008
  20. 2022/5/12 选股记录

热门文章

  1. 华为麒麟9000性能提升幅度大,但恐难成安卓一哥
  2. UTC和东八区时间转换(python)
  3. CDH安装Tez 0.8.5
  4. 4大私域流量体系(个人号、公众号、社群和小程序)全方面价值对比:私域流量,企业保命之本爆发之源!...
  5. MMDetection3D 1.1:统一易用的 3D 感知平台
  6. 从坚果3的发布来看,锤子未来的发展将依然艰难
  7. 面对电车难题,自动驾驶会怎么选?
  8. 函函函函函函函函函函函数——two
  9. 字节跳动三场技术面+HR面,掌握这些知识点再也不怕面试通不过!
  10. 安卓虚拟摄像头_iPhone 的第四颗摄像头位置,为什么给了激光雷达?