package com.xuqm.tenant.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.xuqm.tenant.entity.OperationLogEntity; import com.xuqm.tenant.repository.OperationLogRepository; import jakarta.servlet.http.HttpServletRequest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.time.LocalDateTime; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; @Service public class OperationLogService { private final OperationLogRepository repository; private final ObjectMapper objectMapper; public OperationLogService(OperationLogRepository repository, ObjectMapper objectMapper) { this.repository = repository; this.objectMapper = objectMapper; } public void record(String tenantId, String moduleType, String resourceType, String resourceId, String action, Map detail) { record(tenantId, moduleType, resourceType, resourceId, action, null, detail); } public void record(String tenantId, String moduleType, String resourceType, String resourceId, String action, String summary, Map detail) { OperationLogEntity entity = new OperationLogEntity(); entity.setId(UUID.randomUUID().toString()); entity.setTenantId(tenantId); entity.setModuleType(moduleType); entity.setResourceType(resourceType); entity.setResourceId(resourceId); entity.setAction(action); entity.setOperator(currentOperator()); entity.setSummary(summary); entity.setIpAddress(resolveClientIp()); entity.setDetailJson(serialize(detail)); entity.setCreatedAt(LocalDateTime.now()); repository.save(entity); } public Page list(String tenantId, String moduleType, int page, int size) { int safePage = Math.max(page, 0); int safeSize = Math.min(Math.max(size, 1), 200); PageRequest pageable = PageRequest.of(safePage, safeSize); if (moduleType == null || moduleType.isBlank()) { return repository.findByTenantIdOrderByCreatedAtDesc(tenantId, pageable); } return repository.findByTenantIdAndModuleTypeOrderByCreatedAtDesc( tenantId, moduleType.trim().toUpperCase(), pageable); } private String currentOperator() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || auth.getName() == null || auth.getName().isBlank()) { return "system"; } if (auth.getDetails() instanceof io.jsonwebtoken.Claims claims) { String nickname = claims.get("nickname", String.class); if (nickname != null && !nickname.isBlank()) return nickname; String username = claims.get("username", String.class); if (username != null && !username.isBlank()) return username; } return auth.getName(); } private String resolveClientIp() { try { ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attrs == null) return null; HttpServletRequest request = attrs.getRequest(); String ip = request.getHeader("X-Forwarded-For"); if (ip != null && !ip.isBlank()) { ip = ip.split(",")[0].trim(); } if (ip == null || ip.isBlank()) { ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.isBlank()) { ip = request.getRemoteAddr(); } return ip; } catch (Exception e) { return null; } } private String serialize(Map detail) { try { Map payload = detail == null ? Map.of() : new LinkedHashMap<>(detail); return objectMapper.writeValueAsString(payload); } catch (Exception e) { return "{}"; } } }