| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- package org.dromara.crm.service.impl;
- import cn.hutool.core.util.StrUtil;
- import cn.hutool.http.useragent.UserAgent;
- import cn.hutool.http.useragent.UserAgentUtil;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.dromara.common.core.utils.MapstructUtils;
- import org.dromara.crm.domain.CrmQuoteShareLog;
- import org.dromara.crm.domain.vo.CrmQuoteShareLogVo;
- import org.dromara.crm.mapper.CrmQuoteShareLogMapper;
- import org.dromara.crm.service.ICrmQuoteShareLogService;
- import org.springframework.stereotype.Service;
- import java.time.LocalDateTime;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.stream.Collectors;
- /**
- * 报价单分享访问日志Service实现
- *
- * @author 系统管理员
- * @date 2025-10-05
- */
- @Slf4j
- @RequiredArgsConstructor
- @Service
- public class CrmQuoteShareLogServiceImpl implements ICrmQuoteShareLogService {
- private final CrmQuoteShareLogMapper baseMapper;
- @Override
- public Boolean recordVisit(Long quoteId, String shareCode, String ipAddress, String userAgent) {
- CrmQuoteShareLog log = new CrmQuoteShareLog();
- log.setQuoteId(quoteId);
- log.setShareCode(shareCode);
- log.setVisitTime(LocalDateTime.now());
- log.setIpAddress(ipAddress);
- log.setPageViews(1);
- log.setStayDuration(0);
- // 解析User-Agent
- if (StrUtil.isNotBlank(userAgent)) {
- try {
- UserAgent ua = UserAgentUtil.parse(userAgent);
- // 设置浏览器信息
- String browser = ua.getBrowser().getName() + " " + ua.getVersion();
- log.setBrowser(browser);
- // 判断设备类型
- if (ua.isMobile()) {
- log.setDeviceType("Mobile");
- } else {
- log.setDeviceType("PC");
- }
- } catch (Exception e) {
- CrmQuoteShareLogServiceImpl.log.warn("解析User-Agent失败: userAgent={}", userAgent, e);
- log.setBrowser(userAgent.length() > 100 ? userAgent.substring(0, 100) : userAgent);
- log.setDeviceType("Unknown");
- }
- } else {
- log.setDeviceType("Unknown");
- }
- // TODO: 根据IP地址解析地理位置(可使用ip2region库)
- // log.setLocation(getLocationByIp(ipAddress));
- CrmQuoteShareLogServiceImpl.log.info("记录报价单访问日志: quoteId={}, shareCode={}, ip={}, device={}",
- quoteId, shareCode, ipAddress, log.getDeviceType());
- return baseMapper.insert(log) > 0;
- }
- @Override
- public List<CrmQuoteShareLogVo> queryLogsByQuoteId(Long quoteId) {
- LambdaQueryWrapper<CrmQuoteShareLog> lqw = Wrappers.lambdaQuery();
- lqw.eq(CrmQuoteShareLog::getQuoteId, quoteId);
- lqw.orderByDesc(CrmQuoteShareLog::getVisitTime);
- List<CrmQuoteShareLog> list = baseMapper.selectList(lqw);
- return MapstructUtils.convert(list, CrmQuoteShareLogVo.class);
- }
- @Override
- public Map<String, Object> statisticsVisits(Long quoteId) {
- LambdaQueryWrapper<CrmQuoteShareLog> lqw = Wrappers.lambdaQuery();
- lqw.eq(CrmQuoteShareLog::getQuoteId, quoteId);
- List<CrmQuoteShareLog> logs = baseMapper.selectList(lqw);
- Map<String, Object> stats = new HashMap<>();
- // 总访问次数
- stats.put("totalVisits", logs.size());
- // 独立IP数
- long uniqueIps = logs.stream()
- .filter(log -> StrUtil.isNotBlank(log.getIpAddress()))
- .map(CrmQuoteShareLog::getIpAddress)
- .distinct()
- .count();
- stats.put("uniqueIps", uniqueIps);
- // 最近访问时间
- LocalDateTime lastVisit = logs.stream()
- .map(CrmQuoteShareLog::getVisitTime)
- .max(LocalDateTime::compareTo)
- .orElse(null);
- stats.put("lastVisitTime", lastVisit);
- // 首次访问时间
- LocalDateTime firstVisit = logs.stream()
- .map(CrmQuoteShareLog::getVisitTime)
- .min(LocalDateTime::compareTo)
- .orElse(null);
- stats.put("firstVisitTime", firstVisit);
- // 设备类型分布
- Map<String, Long> deviceStats = logs.stream()
- .collect(Collectors.groupingBy(
- log -> StrUtil.isNotBlank(log.getDeviceType()) ? log.getDeviceType() : "Unknown",
- Collectors.counting()));
- stats.put("deviceStats", deviceStats);
- // 总停留时长
- int totalStayDuration = logs.stream()
- .mapToInt(log -> log.getStayDuration() != null ? log.getStayDuration() : 0)
- .sum();
- stats.put("totalStayDuration", totalStayDuration);
- // 平均停留时长
- double avgStayDuration = logs.isEmpty() ? 0 : (double) totalStayDuration / logs.size();
- stats.put("avgStayDuration", avgStayDuration);
- log.info("统计报价单访问数据: quoteId={}, totalVisits={}, uniqueIps={}",
- quoteId, stats.get("totalVisits"), stats.get("uniqueIps"));
- return stats;
- }
- // TODO: 实现IP地址解析地理位置功能
- // private String getLocationByIp(String ipAddress) {
- // // 使用ip2region等库解析IP地址
- // return null;
- // }
- }
|