本文最后更新于 2026年5月17日 中午
升级记录访问日志接口
前言
在先前的访问记录中没有对操作人信息和地区信息进行收集
在数据表中新增 operator 和 region 字段用于记录
也方便后台记录
代码实现
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
@SuppressWarnings("serial") @Data @AllArgsConstructor @NoArgsConstructor @TableName("visit_log") public class VisitLog extends Model<VisitLog> { @TableId private Long id; private String ip; private String operator; private String pageUrl; private String pageTitle; private String referrer; private String deviceType; private String region; private String browser; private String os; private Date visitTime; private Long executionTime;
}
|
在 SiteStatisticsServiceImpl 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| @Override public ResponseResult recordVisit(RecordVisitDto recordVisitDto, HttpServletRequest request) { long startTime = System.currentTimeMillis(); String ip = IpUtils.getIpAddr(request); String userAgent = request.getHeader("User-Agent"); if (userAgent == null) { userAgent = ""; }
String rateLimitKey = "rate:recordVisit:" + ip + ":" + (System.currentTimeMillis() / 60000); Long count = redisTemplate.opsForValue().increment(rateLimitKey); if (count == 1) { redisTemplate.expire(rateLimitKey, 70, java.util.concurrent.TimeUnit.SECONDS); } if (count > 30) { throw new SystemException(AppHttpCodeEnum.IP_REQUEST_LIMIT); }
String maskedIp = maskIp(ip);
String operator = SystemConstants.OPERATOR_IS_VISITOR; if (SecurityUtils.isLogin()) { operator = Objects.requireNonNull(SecurityUtils.getLoginUser()).getUsername(); }
String region = IpLocationUtils.getCityByIp(ip); if (region == null || region.isBlank()) { region = SystemConstants.UNKNOWN; }
VisitLog visitLog = new VisitLog(); visitLog.setIp(maskedIp); visitLog.setOperator(operator); visitLog.setRegion(region);
if (!userAgent.isEmpty()) { visitLog.setDeviceType(parseDeviceType(userAgent)); visitLog.setBrowser(parseBrowser(userAgent)); visitLog.setOs(parseOs(userAgent)); }
String pageUrl = recordVisitDto.getPageUrl(); if (pageUrl != null && pageUrl.contains("?")) { pageUrl = pageUrl.substring(0, pageUrl.indexOf('?')); } visitLog.setPageUrl(pageUrl); visitLog.setPageTitle(recordVisitDto.getPageTitle()); visitLog.setReferrer(recordVisitDto.getReferrer()); visitLog.setMethod(request.getMethod()); visitLog.setVisitTime(new Date()); visitLog.setExecutionTime((int) (System.currentTimeMillis() - startTime)); redisCache.pushToList(SystemConstants.VISIT_LOG_QUEUE, visitLog);
redisCache.incrementCacheMapValue(SystemConstants.SITE_TOTAL_VIEWS, SystemConstants.SITE_TOTAL_VIEWS_FIELD, 1);
String visitorHash = DigestUtils.md5DigestAsHex((ip + userAgent).getBytes(StandardCharsets.UTF_8)); String visitorKey = SystemConstants.SITE_VISITOR_SET_PREFIX;
boolean isNewVisitor = redisCache.addToSet(visitorKey, visitorHash); if (isNewVisitor) { redisCache.incrementCacheMapValue(SystemConstants.SITE_TOTAL_VISITORS, SystemConstants.SITE_TOTAL_VISITORS_FIELD, 1);
UniqueVisitor uniqueVisitor = new UniqueVisitor(); uniqueVisitor.setVisitorHash(visitorHash); uniqueVisitor.setIp(ip); uniqueVisitor.setFirstVisitTime(new Date()); uniqueVisitor.setLastVisitTime(new Date()); uniqueVisitor.setVisitCount(1); uniqueVisitorMapper.insert(uniqueVisitor); } else { UniqueVisitor uniqueVisitor = uniqueVisitorMapper.selectOne(new LambdaQueryWrapper<UniqueVisitor>() .eq(UniqueVisitor::getVisitorHash, visitorHash) .last("limit 1")); if (uniqueVisitor != null) { uniqueVisitor.setIp(ip); uniqueVisitor.setLastVisitTime(new Date()); uniqueVisitor.setVisitCount(uniqueVisitor.getVisitCount() == null ? 1 : uniqueVisitor.getVisitCount() + 1); uniqueVisitorMapper.updateById(uniqueVisitor); } }
return ResponseResult.okResult(); }
|
添加常量
1 2 3 4 5 6 7 8
|
public static final String OPERATOR_IS_VISITOR = "游客";
public static final String UNKNOWN = "unknown";
|
PS:该系列只做为作者学习开发项目做的笔记用
不一定符合读者来学习,仅供参考
预告
后续会记录博客的开发过程
每次学习会做一份笔记来进行发表
“一花一世界,一叶一菩提”
版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/