package cn.iocoder.yudao.module.mes.service.trace.consumer; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.digest.DigestUtil; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; import cn.iocoder.yudao.framework.ip.core.utils.IPUtils; import cn.iocoder.yudao.module.mes.dal.dataobject.trace.consumer.MesTraceConsumerScanEventDO; import cn.iocoder.yudao.module.mes.dal.mysql.trace.consumer.MesTraceConsumerScanEventMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; /** * 消费者扫码访问事件 Service 实现 * * @author 超级管理员 */ @Service @Slf4j public class MesTraceConsumerScanEventServiceImpl implements MesTraceConsumerScanEventService { private static final int TRACE_CODE_MAX_LENGTH = 255; private static final int NORMALIZED_CODE_MAX_LENGTH = 128; @Resource private MesTraceConsumerScanEventMapper eventMapper; @Override public MesTraceConsumerScanEventDO recordScan(String traceCode, String normalizedCode, String sourceType, Long batchId, boolean success, String errorCode) { try { MesTraceConsumerScanEventDO event = new MesTraceConsumerScanEventDO(); event.setTraceCode(limit(traceCode, TRACE_CODE_MAX_LENGTH)); event.setNormalizedCode(limit(normalizedCode, NORMALIZED_CODE_MAX_LENGTH)); event.setSourceType(sourceType); event.setChannel(limit(channel(), 32)); String[] region = region(ServletUtils.getClientIP()); event.setProvince(limit(region[0], 64)); event.setCity(limit(region[1], 64)); event.setBatchId(batchId); event.setSuccess(success); event.setErrorCode(limit(errorCode, 64)); event.setIpHash(hash(ServletUtils.getClientIP())); event.setUserAgentHash(hash(ServletUtils.getUserAgent())); event.setEventTime(LocalDateTime.now()); eventMapper.insert(event); return event; } catch (Exception ex) { log.warn("[recordScan][记录消费者扫码访问事件失败] traceCode={}", limit(traceCode, 64), ex); return null; } } @Override public MesTraceConsumerScanEventDO getEvent(Long id) { return eventMapper.selectById(id); } /** * 根据 User-Agent 识别扫码入口渠道 */ private String channel() { String ua = ServletUtils.getUserAgent(); if (StrUtil.isBlank(ua)) { return null; } String lower = ua.toLowerCase(); if (lower.contains("micromessenger")) { return "微信"; } if (lower.contains("alipayclient")) { return "支付宝"; } return "浏览器"; } /** * 根据客户端 IP 解析省份、城市;解析失败返回空,不影响事件记录 */ private String[] region(String ip) { if (StrUtil.isBlank(ip)) { return new String[]{null, null}; } Area area = IPUtils.getArea(ip); if (area == null) { return new String[]{null, null}; } String province = null; String city = null; Area node = area; while (node != null) { Integer type = node.getType(); if (AreaTypeEnum.PROVINCE.getType().equals(type)) { province = node.getName(); } else if (AreaTypeEnum.CITY.getType().equals(type)) { city = node.getName(); } node = node.getParent(); } return new String[]{province, city}; } private String hash(String value) { return value == null || value.isBlank() ? null : DigestUtil.sha256Hex(value); } private String limit(String value, int maxLength) { if (value == null || value.length() <= maxLength) { return value; } return value.substring(0, maxLength); } }