2026-04-30 09:49:05 +08:00
|
|
|
package com.xuqm.tenant.service;
|
|
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import com.xuqm.tenant.entity.OperationLogEntity;
|
|
|
|
|
import com.xuqm.tenant.repository.OperationLogRepository;
|
2026-05-27 12:27:42 +08:00
|
|
|
import jakarta.servlet.http.HttpServletRequest;
|
2026-04-30 09:49:05 +08:00
|
|
|
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;
|
2026-05-27 12:27:42 +08:00
|
|
|
import org.springframework.web.context.request.RequestContextHolder;
|
|
|
|
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
2026-04-30 09:49:05 +08:00
|
|
|
|
|
|
|
|
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<String, Object> detail) {
|
2026-05-27 12:27:42 +08:00
|
|
|
record(tenantId, moduleType, resourceType, resourceId, action, null, detail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void record(String tenantId,
|
|
|
|
|
String moduleType,
|
|
|
|
|
String resourceType,
|
|
|
|
|
String resourceId,
|
|
|
|
|
String action,
|
|
|
|
|
String summary,
|
|
|
|
|
Map<String, Object> detail) {
|
2026-04-30 09:49:05 +08:00
|
|
|
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());
|
2026-05-27 12:27:42 +08:00
|
|
|
entity.setSummary(summary);
|
|
|
|
|
entity.setIpAddress(resolveClientIp());
|
2026-04-30 09:49:05 +08:00
|
|
|
entity.setDetailJson(serialize(detail));
|
|
|
|
|
entity.setCreatedAt(LocalDateTime.now());
|
|
|
|
|
repository.save(entity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Page<OperationLogEntity> 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";
|
|
|
|
|
}
|
2026-05-27 13:36:16 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-04-30 09:49:05 +08:00
|
|
|
return auth.getName();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 12:27:42 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 09:49:05 +08:00
|
|
|
private String serialize(Map<String, Object> detail) {
|
|
|
|
|
try {
|
|
|
|
|
Map<String, Object> payload = detail == null ? Map.of() : new LinkedHashMap<>(detail);
|
|
|
|
|
return objectMapper.writeValueAsString(payload);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return "{}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|