XuqmGroup-Server/tenant-service/src/main/java/com/xuqm/tenant/service/OperationLogService.java
XuqmGroup 73dd4814f2 feat(logs): 添加操作日志功能支持推送和授权模块
- 在JwtAuthFilter中设置认证详情到claims
- 为license-service添加LicenseOperationLog相关实体、仓库和服务
- 为push-service添加PushOperationLog相关实体、仓库和服务
- 在LicenseAdminController中注入并使用操作日志记录授权变更
- 在PushManagementController中注入并使用操作日志记录推送操作
- 更新OperationLogService以支持从JWT claims获取用户信息
- 扩展OperationLogService支持推送和授权操作日志查询
- 在前端OperationLogView中添加推送和授权日志选项卡
- 添加LicenseOperationLog和PushOperationLog接口定义
- 实现推送和授权日志的数据加载和分页功能
- 添加操作类型和资源类型的标签映射支持
2026-05-27 13:36:16 +08:00

119 行
4.5 KiB
Java

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<String, Object> 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<String, Object> 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<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";
}
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<String, Object> detail) {
try {
Map<String, Object> payload = detail == null ? Map.of() : new LinkedHashMap<>(detail);
return objectMapper.writeValueAsString(payload);
} catch (Exception e) {
return "{}";
}
}
}