diff --git a/capability/src/main/java/com/sdm/capability/controller/FlowController.java b/capability/src/main/java/com/sdm/capability/controller/FlowController.java
index 10a86d24..70baea21 100644
--- a/capability/src/main/java/com/sdm/capability/controller/FlowController.java
+++ b/capability/src/main/java/com/sdm/capability/controller/FlowController.java
@@ -9,6 +9,7 @@ import com.sdm.common.common.SdmResponse;
import com.sdm.common.entity.req.capability.FlowNodeDto;
import com.sdm.common.entity.req.system.LaunchApproveReq;
import com.sdm.common.feign.inter.capability.ISimulationFlowFeignClient;
+import com.sdm.common.log.annotation.SysLog;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -35,6 +36,7 @@ public class FlowController implements ISimulationFlowFeignClient {
* @param template
* @return
*/
+ @SysLog("添加仿真流程模版草稿")
@PostMapping("/upgradeFlowTemplateDraft")
public SdmResponse upgradeFlowTemplateDraft(@RequestBody @Validated SimulationFlowTemplate template) {
return IFlowService.upgradeFlowTemplateDraft(template);
@@ -45,6 +47,7 @@ public class FlowController implements ISimulationFlowFeignClient {
* @param template
* @return
*/
+ @SysLog("弃用模板")
@PostMapping("/updateFlowTemplateDraft")
public SdmResponse updateFlowTemplateDraft(@RequestBody @Validated SimulationFlowTemplate template) {
return IFlowService.updateFlowTemplateDraft(template);
@@ -55,11 +58,13 @@ public class FlowController implements ISimulationFlowFeignClient {
* @param flowTemplate
* @return
*/
+ @SysLog("删除流程模版草稿")
@PostMapping("/deleteFlowTemplateDraft")
public SdmResponse deleteFlowTemplateDraft(@RequestBody SimulationFlowTemplate flowTemplate) {
return IFlowService.deleteFlowTemplateDraft(flowTemplate.uuid);
}
+ @SysLog("发布流程模版草稿")
@PostMapping("/releaseFlowTemplate")
public SdmResponse releaseFlowTemplateDraft(@RequestBody @Validated ReleaseFlowTemplateReq req) {
return IFlowService.releaseFlowTemplate(req.uuid,req.versionType,req.approveTemplateName,req.approveTemplateId);
diff --git a/capability/src/main/java/com/sdm/capability/controller/TaskPoolController.java b/capability/src/main/java/com/sdm/capability/controller/TaskPoolController.java
index 4b581618..e2059501 100644
--- a/capability/src/main/java/com/sdm/capability/controller/TaskPoolController.java
+++ b/capability/src/main/java/com/sdm/capability/controller/TaskPoolController.java
@@ -4,6 +4,7 @@ import com.sdm.capability.model.req.taskPool.DelTaskPoolReq;
import com.sdm.capability.model.req.taskPool.QueryTaskPoolReq;
import com.sdm.capability.service.TaskPoolService;
import com.sdm.common.common.SdmResponse;
+import com.sdm.common.log.annotation.SysLog;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -22,6 +23,7 @@ public class TaskPoolController {
* @param req req
* @return SdmResponse
*/
+ @SysLog("删除任务库")
@PostMapping("/delTaskPool")
public SdmResponse delTaskPool(@RequestBody @Validated DelTaskPoolReq req) {
return taskPoolService.delTaskPool(req);
diff --git a/common/pom.xml b/common/pom.xml
index 7b7b35c0..788c1fc7 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -58,6 +58,12 @@
springdoc-openapi-starter-webmvc-ui
+
+
+ org.aspectj
+ aspectjrt
+
+
jakarta.servlet
diff --git a/common/src/main/java/com/sdm/common/feign/impl/system/SysLogFeignClientImpl.java b/common/src/main/java/com/sdm/common/feign/impl/system/SysLogFeignClientImpl.java
new file mode 100644
index 00000000..186b5746
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/feign/impl/system/SysLogFeignClientImpl.java
@@ -0,0 +1,36 @@
+package com.sdm.common.feign.impl.system;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.sdm.common.common.SdmResponse;
+import com.sdm.common.feign.inter.system.ISysLogFeignClient;
+import com.sdm.common.log.dto.SysLogDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+
+@Slf4j
+@Component
+public class SysLogFeignClientImpl implements ISysLogFeignClient {
+
+ @Autowired
+ private ISysLogFeignClient sysLogFeignClient;
+
+ @Override
+ public SdmResponse saveLog(SysLogDTO req) {
+ SdmResponse response=null ;
+ try {
+ response = sysLogFeignClient.saveLog(req);
+ if(response==null || !response.isSuccess()){
+ log.error("saveLog failed response:{}", JSONObject.toJSONString(Optional.ofNullable(response)));
+ return SdmResponse.failed("记录日志失败");
+ }
+ } catch (Exception e) {
+ log.error("saveLog error response:{}", JSONObject.toJSONString(Optional.ofNullable(response)));
+ return SdmResponse.failed("记录日志异常");
+ }
+ return response;
+ }
+}
diff --git a/common/src/main/java/com/sdm/common/feign/inter/system/ISysLogFeignClient.java b/common/src/main/java/com/sdm/common/feign/inter/system/ISysLogFeignClient.java
new file mode 100644
index 00000000..d825747e
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/feign/inter/system/ISysLogFeignClient.java
@@ -0,0 +1,15 @@
+package com.sdm.common.feign.inter.system;
+
+import com.sdm.common.common.SdmResponse;
+import com.sdm.common.log.dto.SysLogDTO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+
+@FeignClient(name = "system",contextId = "systemLogClient")
+public interface ISysLogFeignClient {
+
+ @PostMapping("/systemLog/saveLog")
+ SdmResponse saveLog(@RequestBody SysLogDTO req);
+}
diff --git a/common/src/main/java/com/sdm/common/log/annotation/SysLog.java b/common/src/main/java/com/sdm/common/log/annotation/SysLog.java
new file mode 100644
index 00000000..a84973e6
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/annotation/SysLog.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright (c) 2018-2025, honeycom All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: honeycom
+ *
+ */
+
+package com.sdm.common.log.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 操作日志注解
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SysLog {
+
+ /**
+ * 描述
+ * @return {String}
+ */
+ String value() default "";
+
+ /**
+ * spel 表达式
+ * @return 日志描述
+ */
+ String expression() default "";
+
+}
diff --git a/common/src/main/java/com/sdm/common/log/aspect/SysLogAspect.java b/common/src/main/java/com/sdm/common/log/aspect/SysLogAspect.java
new file mode 100644
index 00000000..f480aca8
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/aspect/SysLogAspect.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sdm.common.log.aspect;
+
+import cn.hutool.core.util.StrUtil;
+import com.sdm.common.common.ThreadLocalContext;
+import com.sdm.common.log.annotation.SysLog;
+import com.sdm.common.log.dto.SysLogDTO;
+import com.sdm.common.log.event.SpringContextHolder;
+import com.sdm.common.log.event.SysLogEvent;
+import com.sdm.common.log.utils.LogTypeEnum;
+import com.sdm.common.log.utils.SysLogUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.stereotype.Component;
+
+/**
+ * 操作日志使用spring event异步入库
+ */
+@Aspect
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class SysLogAspect {
+
+ @Around("@annotation(sysLog)")
+ @SneakyThrows
+ public Object around(ProceedingJoinPoint point, SysLog sysLog) {
+ String strClassName = point.getTarget().getClass().getName();
+ String strMethodName = point.getSignature().getName();
+ log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
+
+ String value = sysLog.value();
+
+ SysLogDTO logVo = SysLogUtils.getSysLog();
+ logVo.setTitle(value);
+ // 获取请求body参数
+ if (StrUtil.isBlank(logVo.getParams())) {
+ logVo.setBody(point.getArgs());
+ }
+ // 发送异步日志事件
+ Long startTime = System.currentTimeMillis();
+ Object obj;
+
+ try {
+ obj = point.proceed();
+ }
+ catch (Exception e) {
+ logVo.setLogType(LogTypeEnum.ERROR.getType());
+ logVo.setException(e.getMessage());
+ throw e;
+ }
+ finally {
+ Long endTime = System.currentTimeMillis();
+ logVo.setTime(endTime - startTime);
+ logVo.setTenantId(ThreadLocalContext.getTenantId());
+ SpringContextHolder.publishEvent(new SysLogEvent(logVo));
+ }
+
+ return obj;
+ }
+
+}
diff --git a/common/src/main/java/com/sdm/common/log/config/SysLogProperties.java b/common/src/main/java/com/sdm/common/log/config/SysLogProperties.java
new file mode 100644
index 00000000..5c16e333
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/config/SysLogProperties.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sdm.common.log.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 日志配置类
+ *
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "sys.log")
+public class SysLogProperties {
+
+ /**
+ * 开启日志记录
+ */
+ private boolean enabled = true;
+
+ /**
+ * 记录请求报文体
+ */
+ private boolean requestEnabled = true;
+
+ /**
+ * 放行字段,password,mobile,idcard,phone
+ */
+ @Value("${log.exclude-fields:password,mobile,idcard,phone}")
+ private List excludeFields;
+
+ /**
+ * 请求报文最大存储长度
+ */
+ private Integer maxLength = 2000;
+
+}
diff --git a/common/src/main/java/com/sdm/common/log/dto/SysLogDTO.java b/common/src/main/java/com/sdm/common/log/dto/SysLogDTO.java
new file mode 100644
index 00000000..7f8f0844
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/dto/SysLogDTO.java
@@ -0,0 +1,98 @@
+package com.sdm.common.log.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 日志查询传输对象
+ */
+@Data
+@Schema(description = "日志查询对象")
+public class SysLogDTO {
+
+ /**
+ * 编号
+ */
+ private Long id;
+
+ /**
+ * 日志类型
+ */
+ @NotBlank(message = "日志类型不能为空")
+ private String logType;
+
+ /**
+ * 日志标题
+ */
+ @NotBlank(message = "日志标题不能为空")
+ private String title;
+
+ /**
+ * 创建者
+ */
+ private String createBy;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 操作IP地址
+ */
+ private String remoteAddr;
+
+ /**
+ * 用户代理
+ */
+ private String userAgent;
+
+ /**
+ * 请求URI
+ */
+ private String requestUri;
+
+ /**
+ * 操作方式
+ */
+ private String method;
+
+ /**
+ * 操作提交的数据
+ */
+ private String params;
+
+ /**
+ * 参数重写成object
+ */
+ private Object body;
+
+ /**
+ * 执行时间
+ */
+ private Long time;
+
+ /**
+ * 异常信息
+ */
+ private String exception;
+
+ /**
+ * 服务ID
+ */
+ private String serviceId;
+
+ /**
+ * 创建时间区间 [开始时间,结束时间]
+ */
+ private LocalDateTime[] createTime;
+
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+
+}
diff --git a/common/src/main/java/com/sdm/common/log/event/SpringContextHolder.java b/common/src/main/java/com/sdm/common/log/event/SpringContextHolder.java
new file mode 100644
index 00000000..6b22081c
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/event/SpringContextHolder.java
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright (c) 2018-2025, honeycom All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: honeycom
+ *
+ */
+
+package com.sdm.common.log.event;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * @author honeycom
+ * Spring 工具类
+ */
+@Slf4j
+@Service
+@Lazy(false)
+public class SpringContextHolder implements BeanFactoryPostProcessor, ApplicationContextAware, DisposableBean {
+
+ private static ConfigurableListableBeanFactory beanFactory;
+
+ private static ApplicationContext applicationContext = null;
+
+ /**
+ * 取得存储在静态变量中的ApplicationContext.
+ */
+ public static ApplicationContext getApplicationContext() {
+ return applicationContext;
+ }
+
+ /**
+ * BeanFactoryPostProcessor, 注入Context到静态变量中.
+ */
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
+ SpringContextHolder.beanFactory = factory;
+ }
+
+ /**
+ * 实现ApplicationContextAware接口, 注入Context到静态变量中.
+ */
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ SpringContextHolder.applicationContext = applicationContext;
+ }
+
+ public static ListableBeanFactory getBeanFactory() {
+ return null == beanFactory ? applicationContext : beanFactory;
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ @SuppressWarnings("unchecked")
+ public static T getBean(String name) {
+ return (T) getBeanFactory().getBean(name);
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, Map>
+ */
+ public static Map getBeansOfType(Class type) {
+ return getBeanFactory().getBeansOfType(type);
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ public static T getBean(Class requiredType) {
+ return getBeanFactory().getBean(requiredType);
+ }
+
+ /**
+ * 清除SpringContextHolder中的ApplicationContext为Null.
+ */
+ public static void clearHolder() {
+ if (log.isDebugEnabled()) {
+ log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
+ }
+ applicationContext = null;
+ }
+
+ /**
+ * 发布事件
+ * @param event
+ */
+ public static void publishEvent(ApplicationEvent event) {
+ if (applicationContext == null) {
+ return;
+ }
+ applicationContext.publishEvent(event);
+ }
+
+ /**
+ * 实现DisposableBean接口, 在Context关闭时清理静态变量.
+ */
+ @Override
+ public void destroy() {
+ SpringContextHolder.clearHolder();
+ }
+
+}
diff --git a/common/src/main/java/com/sdm/common/log/event/SysLogEvent.java b/common/src/main/java/com/sdm/common/log/event/SysLogEvent.java
new file mode 100644
index 00000000..69a31671
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/event/SysLogEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sdm.common.log.event;
+
+import com.sdm.common.log.dto.SysLogDTO;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author honeycom 系统日志事件
+ */
+public class SysLogEvent extends ApplicationEvent {
+
+ public SysLogEvent(SysLogDTO source) {
+ super(source);
+ }
+
+}
diff --git a/common/src/main/java/com/sdm/common/log/event/SysLogListener.java b/common/src/main/java/com/sdm/common/log/event/SysLogListener.java
new file mode 100644
index 00000000..4fd00fe7
--- /dev/null
+++ b/common/src/main/java/com/sdm/common/log/event/SysLogListener.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sdm.common.log.event;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.annotation.JsonFilter;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
+import com.sdm.common.feign.inter.system.ISysLogFeignClient;
+import com.sdm.common.log.config.SysLogProperties;
+import com.sdm.common.log.dto.SysLogDTO;
+import com.sdm.common.log.utils.JavaTimeModule;
+import jakarta.servlet.ServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author honeycom 异步监听日志事件
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Component
+public class SysLogListener implements InitializingBean {
+
+ /**
+ * 忽略序列化的对象类型
+ */
+ private final static Class[] ignoreClass = { ServletRequest.class, BindingResult.class };
+
+ /**
+ * new 一个 避免日志脱敏策略影响全局ObjectMapper
+ */
+ private final static ObjectMapper objectMapper = new ObjectMapper();
+
+ private final ISysLogFeignClient sysLogFeignClient;
+
+ private final SysLogProperties logProperties;
+
+ @SneakyThrows
+ @Async
+ @Order
+ @EventListener(SysLogEvent.class)
+ public void saveSysLog(SysLogEvent event) {
+ SysLogDTO source = (SysLogDTO) event.getSource();
+
+ // json 格式刷参数放在异步中处理,提升性能
+ if (Objects.nonNull(source.getBody()) && logProperties.isRequestEnabled()) {
+ Object[] args = (Object[]) source.getBody();
+ List