mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-25 00:15:06 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@ -25,7 +25,6 @@
|
||||
|
||||
<module>yudao-spring-boot-starter-excel</module>
|
||||
<module>yudao-spring-boot-starter-test</module>
|
||||
<module>yudao-spring-boot-starter-extension</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-biz-operatelog</module>
|
||||
<module>yudao-spring-boot-starter-biz-dict</module>
|
||||
|
@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.dict.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DictDataFrameworkService {
|
||||
|
||||
/**
|
||||
@ -24,12 +22,4 @@ public interface DictDataFrameworkService {
|
||||
*/
|
||||
DictDataRespDTO parseDictDataFromCache(String type, String label);
|
||||
|
||||
/**
|
||||
* 获得指定类型的字典数据,从缓存中
|
||||
*
|
||||
* @param type 字典类型
|
||||
* @return 字典数据列表
|
||||
*/
|
||||
List<DictDataRespDTO> listDictDatasFromCache(String type);
|
||||
|
||||
}
|
||||
|
@ -34,6 +34,13 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
|
@ -1,6 +1,9 @@
|
||||
package cn.iocoder.yudao.framework.operatelog.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkServiceImpl;
|
||||
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@ -12,4 +15,9 @@ public class YudaoOperateLogAutoConfiguration {
|
||||
return new OperateLogAspect();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) {
|
||||
return new OperateLogFrameworkServiceImpl(operateLogApi);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,9 +9,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLog;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import com.google.common.collect.Maps;
|
||||
@ -57,13 +56,13 @@ public class OperateLogAspect {
|
||||
/**
|
||||
* 用于记录操作内容的上下文
|
||||
*
|
||||
* @see OperateLogCreateReqDTO#getContent()
|
||||
* @see OperateLog#getContent()
|
||||
*/
|
||||
private static final ThreadLocal<String> CONTENT = new ThreadLocal<>();
|
||||
/**
|
||||
* 用于记录拓展字段的上下文
|
||||
*
|
||||
* @see OperateLogCreateReqDTO#getExts()
|
||||
* @see OperateLog#getExts()
|
||||
*/
|
||||
private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();
|
||||
|
||||
@ -73,16 +72,20 @@ public class OperateLogAspect {
|
||||
@Around("@annotation(apiOperation)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, ApiOperation apiOperation) throws Throwable {
|
||||
// 可能也添加了 @ApiOperation 注解
|
||||
OperateLog operateLog = getMethodAnnotation(joinPoint, OperateLog.class);
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog = getMethodAnnotation(joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog.class);
|
||||
return around0(joinPoint, operateLog, apiOperation);
|
||||
}
|
||||
|
||||
@Around("!@annotation(io.swagger.annotations.ApiOperation) && @annotation(operateLog)") // 兼容处理,只添加 @OperateLog 注解的情况
|
||||
public Object around(ProceedingJoinPoint joinPoint, OperateLog operateLog) throws Throwable {
|
||||
public Object around(ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) throws Throwable {
|
||||
return around0(joinPoint, operateLog, null);
|
||||
}
|
||||
|
||||
private Object around0(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation) throws Throwable {
|
||||
private Object around0(ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
||||
ApiOperation apiOperation) throws Throwable {
|
||||
// 目前,只有管理员,才记录操作日志!所以非管理员,直接调用,不进行记录
|
||||
Integer userType = WebFrameworkUtils.getLoginUserType();
|
||||
if (!Objects.equals(userType, UserTypeEnum.ADMIN.getValue())) {
|
||||
@ -121,7 +124,9 @@ public class OperateLogAspect {
|
||||
EXTS.remove();
|
||||
}
|
||||
|
||||
private void log(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation,
|
||||
private void log(ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
||||
ApiOperation apiOperation,
|
||||
Date startTime, Object result, Throwable exception) {
|
||||
try {
|
||||
// 判断不记录的情况
|
||||
@ -136,113 +141,119 @@ public class OperateLogAspect {
|
||||
}
|
||||
}
|
||||
|
||||
private void log0(ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation,
|
||||
private void log0(ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
||||
ApiOperation apiOperation,
|
||||
Date startTime, Object result, Throwable exception) {
|
||||
OperateLogCreateReqDTO operateLogDTO = new OperateLogCreateReqDTO();
|
||||
OperateLog operateLogObj = new OperateLog();
|
||||
// 补全通用字段
|
||||
operateLogDTO.setTraceId(TracerUtils.getTraceId());
|
||||
operateLogDTO.setStartTime(startTime);
|
||||
operateLogObj.setTraceId(TracerUtils.getTraceId());
|
||||
operateLogObj.setStartTime(startTime);
|
||||
// 补充用户信息
|
||||
fillUserFields(operateLogDTO);
|
||||
fillUserFields(operateLogObj);
|
||||
// 补全模块信息
|
||||
fillModuleFields(operateLogDTO, joinPoint, operateLog, apiOperation);
|
||||
fillModuleFields(operateLogObj, joinPoint, operateLog, apiOperation);
|
||||
// 补全请求信息
|
||||
fillRequestFields(operateLogDTO);
|
||||
fillRequestFields(operateLogObj);
|
||||
// 补全方法信息
|
||||
fillMethodFields(operateLogDTO, joinPoint, operateLog, startTime, result, exception);
|
||||
fillMethodFields(operateLogObj, joinPoint, operateLog, startTime, result, exception);
|
||||
|
||||
// 异步记录日志
|
||||
operateLogFrameworkService.createOperateLogAsync(operateLogDTO);
|
||||
operateLogFrameworkService.createOperateLog(operateLogObj);
|
||||
}
|
||||
|
||||
private static void fillUserFields(OperateLogCreateReqDTO operateLogDTO) {
|
||||
operateLogDTO.setUserId(WebFrameworkUtils.getLoginUserId());
|
||||
operateLogDTO.setUserType(WebFrameworkUtils.getLoginUserType());
|
||||
private static void fillUserFields(OperateLog operateLogObj) {
|
||||
operateLogObj.setUserId(WebFrameworkUtils.getLoginUserId());
|
||||
operateLogObj.setUserType(WebFrameworkUtils.getLoginUserType());
|
||||
}
|
||||
|
||||
private static void fillModuleFields(OperateLogCreateReqDTO operateLogDTO,
|
||||
ProceedingJoinPoint joinPoint, OperateLog operateLog, ApiOperation apiOperation) {
|
||||
private static void fillModuleFields(OperateLog operateLogObj,
|
||||
ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
||||
ApiOperation apiOperation) {
|
||||
// module 属性
|
||||
if (operateLog != null) {
|
||||
operateLogDTO.setModule(operateLog.module());
|
||||
operateLogObj.setModule(operateLog.module());
|
||||
}
|
||||
if (StrUtil.isEmpty(operateLogDTO.getModule())) {
|
||||
if (StrUtil.isEmpty(operateLogObj.getModule())) {
|
||||
Api api = getClassAnnotation(joinPoint, Api.class);
|
||||
if (api != null) {
|
||||
// 优先读取 @API 的 name 属性
|
||||
if (StrUtil.isNotEmpty(api.value())) {
|
||||
operateLogDTO.setModule(api.value());
|
||||
operateLogObj.setModule(api.value());
|
||||
}
|
||||
// 没有的话,读取 @API 的 tags 属性
|
||||
if (StrUtil.isEmpty(operateLogDTO.getModule()) && ArrayUtil.isNotEmpty(api.tags())) {
|
||||
operateLogDTO.setModule(api.tags()[0]);
|
||||
if (StrUtil.isEmpty(operateLogObj.getModule()) && ArrayUtil.isNotEmpty(api.tags())) {
|
||||
operateLogObj.setModule(api.tags()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// name 属性
|
||||
if (operateLog != null) {
|
||||
operateLogDTO.setName(operateLog.name());
|
||||
operateLogObj.setName(operateLog.name());
|
||||
}
|
||||
if (StrUtil.isEmpty(operateLogDTO.getName()) && apiOperation != null) {
|
||||
operateLogDTO.setName(apiOperation.value());
|
||||
if (StrUtil.isEmpty(operateLogObj.getName()) && apiOperation != null) {
|
||||
operateLogObj.setName(apiOperation.value());
|
||||
}
|
||||
// type 属性
|
||||
if (operateLog != null && ArrayUtil.isNotEmpty(operateLog.type())) {
|
||||
operateLogDTO.setType(operateLog.type()[0].getType());
|
||||
operateLogObj.setType(operateLog.type()[0].getType());
|
||||
}
|
||||
if (operateLogDTO.getType() == null) {
|
||||
if (operateLogObj.getType() == null) {
|
||||
RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
|
||||
OperateTypeEnum operateLogType = convertOperateLogType(requestMethod);
|
||||
operateLogDTO.setType(operateLogType != null ? operateLogType.getType() : null);
|
||||
operateLogObj.setType(operateLogType != null ? operateLogType.getType() : null);
|
||||
}
|
||||
// content 和 exts 属性
|
||||
operateLogDTO.setContent(CONTENT.get());
|
||||
operateLogDTO.setExts(EXTS.get());
|
||||
operateLogObj.setContent(CONTENT.get());
|
||||
operateLogObj.setExts(EXTS.get());
|
||||
}
|
||||
|
||||
private static void fillRequestFields(OperateLogCreateReqDTO operateLogDTO) {
|
||||
private static void fillRequestFields(OperateLog operateLogObj) {
|
||||
// 获得 Request 对象
|
||||
HttpServletRequest request = ServletUtils.getRequest();
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
// 补全请求信息
|
||||
operateLogDTO.setRequestMethod(request.getMethod());
|
||||
operateLogDTO.setRequestUrl(request.getRequestURI());
|
||||
operateLogDTO.setUserIp(ServletUtil.getClientIP(request));
|
||||
operateLogDTO.setUserAgent(ServletUtils.getUserAgent(request));
|
||||
operateLogObj.setRequestMethod(request.getMethod());
|
||||
operateLogObj.setRequestUrl(request.getRequestURI());
|
||||
operateLogObj.setUserIp(ServletUtil.getClientIP(request));
|
||||
operateLogObj.setUserAgent(ServletUtils.getUserAgent(request));
|
||||
}
|
||||
|
||||
private static void fillMethodFields(OperateLogCreateReqDTO operateLogDTO,
|
||||
ProceedingJoinPoint joinPoint, OperateLog operateLog,
|
||||
private static void fillMethodFields(OperateLog operateLogObj,
|
||||
ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog,
|
||||
Date startTime, Object result, Throwable exception) {
|
||||
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
||||
operateLogDTO.setJavaMethod(methodSignature.toString());
|
||||
operateLogObj.setJavaMethod(methodSignature.toString());
|
||||
if (operateLog == null || operateLog.logArgs()) {
|
||||
operateLogDTO.setJavaMethodArgs(obtainMethodArgs(joinPoint));
|
||||
operateLogObj.setJavaMethodArgs(obtainMethodArgs(joinPoint));
|
||||
}
|
||||
if (operateLog == null || operateLog.logResultData()) {
|
||||
operateLogDTO.setResultData(obtainResultData(result));
|
||||
operateLogObj.setResultData(obtainResultData(result));
|
||||
}
|
||||
operateLogDTO.setDuration((int) (System.currentTimeMillis() - startTime.getTime()));
|
||||
operateLogObj.setDuration((int) (System.currentTimeMillis() - startTime.getTime()));
|
||||
// (正常)处理 resultCode 和 resultMsg 字段
|
||||
if (result != null) {
|
||||
if (result instanceof CommonResult) {
|
||||
CommonResult<?> commonResult = (CommonResult<?>) result;
|
||||
operateLogDTO.setResultCode(commonResult.getCode());
|
||||
operateLogDTO.setResultMsg(commonResult.getMsg());
|
||||
operateLogObj.setResultCode(commonResult.getCode());
|
||||
operateLogObj.setResultMsg(commonResult.getMsg());
|
||||
} else {
|
||||
operateLogDTO.setResultCode(SUCCESS.getCode());
|
||||
operateLogObj.setResultCode(SUCCESS.getCode());
|
||||
}
|
||||
}
|
||||
// (异常)处理 resultCode 和 resultMsg 字段
|
||||
if (exception != null) {
|
||||
operateLogDTO.setResultCode(INTERNAL_SERVER_ERROR.getCode());
|
||||
operateLogDTO.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
|
||||
operateLogObj.setResultCode(INTERNAL_SERVER_ERROR.getCode());
|
||||
operateLogObj.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
|
||||
private static boolean isLogEnable(ProceedingJoinPoint joinPoint,
|
||||
cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) {
|
||||
// 有 @OperateLog 注解的情况下
|
||||
if (operateLog != null) {
|
||||
return operateLog.enable();
|
||||
@ -256,9 +267,9 @@ public class OperateLogAspect {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(requestMethods).filter(requestMethod ->
|
||||
requestMethod == RequestMethod.POST
|
||||
|| requestMethod == RequestMethod.PUT
|
||||
|| requestMethod == RequestMethod.DELETE)
|
||||
requestMethod == RequestMethod.POST
|
||||
|| requestMethod == RequestMethod.PUT
|
||||
|| requestMethod == RequestMethod.DELETE)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
@ -1,87 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.operatelog.core.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志创建 Request DTO
|
||||
*/
|
||||
@Data
|
||||
public class OperateLogCreateReqDTO {
|
||||
|
||||
@ApiModelProperty(value = "链路追踪编号", required = true, example = "89aca178-a370-411c-ae02-3f0d672be4ab")
|
||||
@NotEmpty(message = "链路追踪编号不能为空")
|
||||
private String traceId;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "1024")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
@ApiModelProperty(value = "用户类型", required = true, example = "1")
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
private Integer userType;
|
||||
|
||||
@ApiModelProperty(value = "操作模块", required = true, example = "订单")
|
||||
@NotEmpty(message = "操作模块不能为空")
|
||||
private String module;
|
||||
|
||||
@ApiModelProperty(value = "操作名", required = true, example = "创建订单")
|
||||
@NotEmpty(message = "操作名")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "操作分类", required = true, example = "1", notes = "参见 SysOperateLogTypeEnum 枚举类")
|
||||
@NotNull(message = "操作分类不能为空")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "拓展字段", example = "{'orderId': 1}")
|
||||
private Map<String, Object> exts;
|
||||
|
||||
@ApiModelProperty(value = "请求方法名", required = true, example = "GET")
|
||||
@NotEmpty(message = "请求方法名不能为空")
|
||||
private String requestMethod;
|
||||
|
||||
@ApiModelProperty(value = "请求地址", required = true, example = "/xxx/yyy")
|
||||
@NotEmpty(message = "请求地址不能为空")
|
||||
private String requestUrl;
|
||||
|
||||
@ApiModelProperty(value = "用户 IP", required = true, example = "127.0.0.1")
|
||||
@NotEmpty(message = "用户 IP 不能为空")
|
||||
private String userIp;
|
||||
|
||||
@ApiModelProperty(value = "浏览器 UserAgent", required = true, example = "Mozilla/5.0")
|
||||
@NotEmpty(message = "浏览器 UserAgent 不能为空")
|
||||
private String userAgent;
|
||||
|
||||
@ApiModelProperty(value = "Java 方法名", required = true, example = "cn.iocoder.yudao.UserController.save(...)")
|
||||
@NotEmpty(message = "Java 方法名不能为空")
|
||||
private String javaMethod;
|
||||
|
||||
@ApiModelProperty(value = "Java 方法的参数")
|
||||
private String javaMethodArgs;
|
||||
|
||||
@ApiModelProperty(value = "开始时间", required = true)
|
||||
@NotNull(message = "开始时间不能为空")
|
||||
private Date startTime;
|
||||
|
||||
@ApiModelProperty(value = "执行时长,单位:毫秒", required = true)
|
||||
@NotNull(message = "执行时长不能为空")
|
||||
private Integer duration;
|
||||
|
||||
@ApiModelProperty(value = "结果码", required = true)
|
||||
@NotNull(message = "结果码不能为空")
|
||||
private Integer resultCode;
|
||||
|
||||
@ApiModelProperty(value = "结果提示")
|
||||
private String resultMsg;
|
||||
|
||||
@ApiModelProperty(value = "结果数据")
|
||||
private String resultData;
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package cn.iocoder.yudao.framework.operatelog.core.service;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class OperateLog {
|
||||
|
||||
/**
|
||||
* 链路追踪编号
|
||||
*/
|
||||
private String traceId;
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
|
||||
/**
|
||||
* 操作模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 操作名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 操作分类
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 操作明细
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 拓展字段
|
||||
*/
|
||||
private Map<String, Object> exts;
|
||||
|
||||
/**
|
||||
* 请求方法名
|
||||
*/
|
||||
private String requestMethod;
|
||||
|
||||
/**
|
||||
* 请求地址
|
||||
*/
|
||||
private String requestUrl;
|
||||
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
private String userIp;
|
||||
|
||||
/**
|
||||
* 浏览器 UserAgent
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
/**
|
||||
* Java 方法名
|
||||
*/
|
||||
private String javaMethod;
|
||||
|
||||
/**
|
||||
* Java 方法的参数
|
||||
*/
|
||||
private String javaMethodArgs;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 执行时长,单位:毫秒
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 结果码
|
||||
*/
|
||||
private Integer resultCode;
|
||||
|
||||
/**
|
||||
* 结果提示
|
||||
*/
|
||||
private String resultMsg;
|
||||
|
||||
/**
|
||||
* 结果数据
|
||||
*/
|
||||
private String resultData;
|
||||
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
package cn.iocoder.yudao.framework.operatelog.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 操作日志 Framework Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface OperateLogFrameworkService {
|
||||
|
||||
/**
|
||||
* 异步记录操作日志
|
||||
* 记录操作日志
|
||||
*
|
||||
* @param reqVO 操作日志请求
|
||||
* @return true: 记录成功,false: 记录失败
|
||||
* @param operateLog 操作日志请求
|
||||
*/
|
||||
Future<Boolean> createOperateLogAsync(OperateLogCreateReqDTO reqVO);
|
||||
void createOperateLog(OperateLog operateLog);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.framework.operatelog.core.service;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
|
||||
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
/**
|
||||
* 操作日志 Framework Service 实现类
|
||||
*
|
||||
* 基于 {@link OperateLogApi} 实现,记录操作日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class OperateLogFrameworkServiceImpl implements OperateLogFrameworkService {
|
||||
|
||||
private final OperateLogApi operateLogApi;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void createOperateLog(OperateLog operateLog) {
|
||||
OperateLogCreateReqDTO reqDTO = BeanUtil.copyProperties(operateLog, OperateLogCreateReqDTO.class);
|
||||
operateLogApi.createOperateLog(reqDTO);
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>yudao-framework</artifactId>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>yudao-spring-boot-starter-extension</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>扩展点组件</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring 核心 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring 核心 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试包 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试包 -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -1,62 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.ExtensionBootstrap;
|
||||
import cn.iocoder.yudao.framework.extension.core.context.ExtensionContext;
|
||||
import cn.iocoder.yudao.framework.extension.core.context.ExtensionContextHolder;
|
||||
import cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor;
|
||||
import cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory;
|
||||
import cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @description 扩展点组件自动装配
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 21:50
|
||||
* @class cn.iocoder.yudao.framework.extension.config.YudaoExtensionAutoConfiguration.java
|
||||
*/
|
||||
@Configuration
|
||||
public class YudaoExtensionAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 组件初始化
|
||||
* @return
|
||||
*/
|
||||
@Bean(initMethod = "init")
|
||||
@ConditionalOnMissingBean(ExtensionBootstrap.class)
|
||||
public ExtensionBootstrap bootstrap() {
|
||||
return new ExtensionBootstrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展点工厂
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ExtensionRegisterFactory.class, ExtensionFactory.class})
|
||||
public ExtensionRegisterFactory registerFactory() {
|
||||
return new ExtensionRegisterFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展组件上下文对象
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ExtensionContextHolder.class, ExtensionContext.class})
|
||||
public ExtensionContextHolder context() {
|
||||
return new ExtensionContextHolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展组件执行器
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ExtensionExecutor.class)
|
||||
public ExtensionExecutor executor() {
|
||||
return new ExtensionExecutor();
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* @description 业务场景 = businessId + useCase + scenario, 用来标识系统中唯一的一个场景<br/>
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 22:19
|
||||
* @class cn.iocoder.yudao.framework.extension.core.BusinessScenario.java
|
||||
*/
|
||||
public class BusinessScenario implements Serializable {
|
||||
|
||||
/**
|
||||
* 默认业务id
|
||||
*/
|
||||
public final static String DEFAULT_BUSINESS_ID = "#defaultBusinessId#";
|
||||
|
||||
/**
|
||||
* 默认用例
|
||||
*/
|
||||
public final static String DEFAULT_USECASE = "#defaultUseCase#";
|
||||
|
||||
/**
|
||||
* 默认场景
|
||||
*/
|
||||
public final static String DEFAULT_SCENARIO = "#defaultScenario#";
|
||||
|
||||
/**
|
||||
* 分隔符
|
||||
*/
|
||||
private final static String DOT_SEPARATOR = ".";
|
||||
|
||||
/**
|
||||
* 业务Id
|
||||
*/
|
||||
private String businessId;
|
||||
|
||||
/**
|
||||
* 用例
|
||||
*/
|
||||
private String useCase;
|
||||
|
||||
/**
|
||||
* 场景
|
||||
*/
|
||||
private String scenario;
|
||||
|
||||
public BusinessScenario() {
|
||||
this.businessId = DEFAULT_BUSINESS_ID;
|
||||
this.useCase = DEFAULT_USECASE;
|
||||
this.scenario = DEFAULT_SCENARIO;
|
||||
}
|
||||
|
||||
public BusinessScenario(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario) {
|
||||
this.businessId = businessId;
|
||||
this.useCase = useCase;
|
||||
this.scenario = scenario;
|
||||
}
|
||||
|
||||
public BusinessScenario(@NotNull String scenario) {
|
||||
this();
|
||||
this.scenario = scenario;
|
||||
}
|
||||
|
||||
public BusinessScenario(@NotNull String useCase, @NotNull String scenario) {
|
||||
this(DEFAULT_BUSINESS_ID, useCase, scenario);
|
||||
}
|
||||
|
||||
public String getBusinessId() {
|
||||
return businessId;
|
||||
}
|
||||
|
||||
public void setBusinessId(String businessId) {
|
||||
this.businessId = businessId;
|
||||
}
|
||||
|
||||
public String getUseCase() {
|
||||
return useCase;
|
||||
}
|
||||
|
||||
public void setUseCase(String useCase) {
|
||||
this.useCase = useCase;
|
||||
}
|
||||
|
||||
public String getScenario() {
|
||||
return scenario;
|
||||
}
|
||||
|
||||
public void setScenario(String scenario) {
|
||||
this.scenario = scenario;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建业务场景
|
||||
* @param businessId
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @return
|
||||
*/
|
||||
public static BusinessScenario valueOf(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario) {
|
||||
return new BusinessScenario(businessId, useCase, scenario);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建业务场景
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @return
|
||||
*/
|
||||
public static BusinessScenario valueOf(@NotNull String useCase, @NotNull String scenario) {
|
||||
return new BusinessScenario(useCase, scenario);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建业务场景
|
||||
* @param scenario
|
||||
* @return
|
||||
*/
|
||||
public static BusinessScenario valueOf(@NotNull String scenario) {
|
||||
return new BusinessScenario(scenario);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务场景唯一标识
|
||||
* @return
|
||||
*/
|
||||
public String getUniqueIdentity(){
|
||||
return new StringJoiner(DOT_SEPARATOR).add(businessId).add(useCase).add(scenario).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BusinessScenario{" +
|
||||
"businessId='" + businessId + '\'' +
|
||||
", useCase='" + useCase + '\'' +
|
||||
", scenario='" + scenario + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-29 00:18
|
||||
* @class cn.iocoder.yudao.framework.extension.core.ExtensionBootstrap.java
|
||||
*/
|
||||
public class ExtensionBootstrap implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* spring 容器
|
||||
*/
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private ExtensionRegisterFactory registerFactory;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
registerFactory.setApplicationContext(applicationContext);
|
||||
registerFactory.register(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.context;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @description 执行器通用方法
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-29 00:38
|
||||
* @class cn.iocoder.yudao.framework.extension.core.context.AbstractComponentExecutor.java
|
||||
*/
|
||||
public abstract class AbstractComponentExecutor {
|
||||
|
||||
/**
|
||||
* ("业务" + "用例" + "场景")执行扩展组件,并返回执行结果
|
||||
* @param targetClazz
|
||||
* @param businessId
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @param function
|
||||
* @param <R>
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String businessId, String useCase, String scenario, Function<T, R> function) {
|
||||
return execute(targetClazz, BusinessScenario.valueOf(businessId, useCase, scenario), function);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ("用例" + "场景")执行扩展组件,并返回执行结果
|
||||
* @param targetClazz
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @param function
|
||||
* @param <R>
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String useCase, String scenario, Function<T, R> function) {
|
||||
return execute(targetClazz, BusinessScenario.valueOf(useCase, scenario), function);
|
||||
}
|
||||
|
||||
/**
|
||||
* ("场景")执行扩展组件,并返回执行结果
|
||||
* @param targetClazz
|
||||
* @param scenario
|
||||
* @param function
|
||||
* @param <R>
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, String scenario, Function<T, R> function) {
|
||||
return execute(targetClazz, BusinessScenario.valueOf(scenario), function);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行扩展组件,并返回执行结果
|
||||
* @param targetClazz
|
||||
* @param businessScenario
|
||||
* @param function
|
||||
* @param <R> Response Type
|
||||
* @param <T> Parameter Type
|
||||
* @return
|
||||
*/
|
||||
public <R, T extends ExtensionPoint> R execute(Class<T> targetClazz, BusinessScenario businessScenario, Function<T, R> function) {
|
||||
T component = locateComponent(targetClazz, businessScenario);
|
||||
return function.apply(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* ("业务" + "用例" + "场景")执行扩展组件,适用于无返回值的业务
|
||||
* @param targetClazz
|
||||
* @param businessId
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @param consumer
|
||||
* @param <T>
|
||||
*/
|
||||
public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String businessId, String useCase, String scenario, Consumer<T> consumer) {
|
||||
accept(targetClazz, BusinessScenario.valueOf(businessId, useCase, scenario), consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* ("场景")执行扩展组件,适用于无返回值的业务
|
||||
* @param targetClazz
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @param consumer
|
||||
* @param <T>
|
||||
*/
|
||||
public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String useCase, String scenario, Consumer<T> consumer) {
|
||||
accept(targetClazz, BusinessScenario.valueOf(useCase, scenario), consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* ("场景")执行扩展组件,适用于无返回值的业务
|
||||
* @param targetClazz
|
||||
* @param scenario
|
||||
* @param consumer
|
||||
* @param <T>
|
||||
*/
|
||||
public <T extends ExtensionPoint> void accept(Class<T> targetClazz, String scenario, Consumer<T> consumer) {
|
||||
accept(targetClazz, BusinessScenario.valueOf(scenario), consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行扩展组件,适用于无返回值的业务
|
||||
* @param targetClazz
|
||||
* @param businessScenario
|
||||
* @param consumer
|
||||
* @param <T> Parameter Type
|
||||
*/
|
||||
public <T extends ExtensionPoint> void accept(Class<T> targetClazz, BusinessScenario businessScenario, Consumer<T> consumer) {
|
||||
T component = locateComponent(targetClazz, businessScenario);
|
||||
consumer.accept(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取/定位扩展点组件
|
||||
* @param targetClazz
|
||||
* @param businessScenario
|
||||
* @param <C>
|
||||
* @return
|
||||
*/
|
||||
protected abstract <C extends ExtensionPoint> C locateComponent(Class<C> targetClazz, BusinessScenario businessScenario);
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.context;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* @description 上下文,包含各个扩展点的相关操作
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 22:15
|
||||
* @class cn.iocoder.yudao.framework.extension.core.context.ExtensionContext.java
|
||||
*/
|
||||
public interface ExtensionContext {
|
||||
|
||||
/**
|
||||
* 根据业务场景唯一标识获取扩展点组件实现类
|
||||
* @param businessId
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T extends ExtensionPoint> T getPoint(String businessId, String useCase, String scenario, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 根据("实例" + "场景")获取扩展点组件实现类,其中:业务id(businessId)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_BUSINESS_ID}
|
||||
* @param useCase
|
||||
* @param scenario
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T extends ExtensionPoint> T getPoint(String useCase, String scenario, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 根据("场景")获取扩展点组件实现类 <br/>
|
||||
* 其中:
|
||||
* 业务id(businessId)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_BUSINESS_ID}
|
||||
* 实例(useCase)= {@linkplain cn.iocoder.yudao.framework.extension.core.BusinessScenario.DEFAULT_USECASE}
|
||||
* @param scenario
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T extends ExtensionPoint> T getPoint(String scenario, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 根据业务场景唯一标识获取扩展点组件实现类
|
||||
* @param businessScenario
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T extends ExtensionPoint> T getPoint(BusinessScenario businessScenario, Class<T> clazz);
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.context;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @description 上下文及扩展点组件工厂的持有类
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-29 00:29
|
||||
* @class cn.iocoder.yudao.framework.extension.core.context.ExtensionContextHolder.java
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ExtensionContextHolder implements ExtensionContext{
|
||||
|
||||
@Autowired
|
||||
private ExtensionFactory factory;
|
||||
|
||||
@Override
|
||||
public <T extends ExtensionPoint> T getPoint(@NotNull String businessId, @NotNull String useCase, @NotNull String scenario, Class<T> clazz) {
|
||||
return getPoint(BusinessScenario.valueOf(businessId, useCase, scenario), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ExtensionPoint> T getPoint(@NotNull String useCase, String scenario, Class<T> clazz) {
|
||||
return getPoint(BusinessScenario.valueOf(useCase, scenario), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ExtensionPoint> T getPoint(@NotNull String scenario, Class<T> clazz) {
|
||||
return getPoint(BusinessScenario.valueOf(scenario), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ExtensionPoint> T getPoint(@NotNull BusinessScenario businessScenario, Class<T> clazz) {
|
||||
return factory.get(businessScenario, clazz);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.context;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @description 扩展组件执行器
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-29 00:32
|
||||
* @class cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor.java
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ExtensionExecutor extends AbstractComponentExecutor{
|
||||
|
||||
@Autowired
|
||||
private ExtensionContextHolder contextHolder;
|
||||
|
||||
|
||||
@Override
|
||||
protected <C extends ExtensionPoint> C locateComponent(Class<C> targetClazz, BusinessScenario businessScenario) {
|
||||
return contextHolder.getPoint(businessScenario, targetClazz);
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.factory;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @description 扩展定义(扩展坐标),标识唯一一个业务场景实现
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 23:14
|
||||
* @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionDefinition.java
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class ExtensionDefinition implements Serializable {
|
||||
|
||||
/**
|
||||
* 业务场景唯一标识(id)
|
||||
*/
|
||||
private String uniqueIdentify;
|
||||
|
||||
/**
|
||||
* 扩展点实现类名称
|
||||
*/
|
||||
private String extensionPointName;
|
||||
|
||||
/**
|
||||
* 业务场景
|
||||
*/
|
||||
private BusinessScenario businessScenario;
|
||||
|
||||
/**
|
||||
* 扩展点实现类
|
||||
*/
|
||||
private ExtensionPoint extensionPoint;
|
||||
|
||||
/**
|
||||
* class
|
||||
*/
|
||||
private Class extensionPointClass;
|
||||
|
||||
public ExtensionDefinition() {
|
||||
}
|
||||
|
||||
public ExtensionDefinition(@NotNull BusinessScenario businessScenario, @NotNull ExtensionPoint extensionPoint) {
|
||||
this.businessScenario = businessScenario;
|
||||
this.extensionPoint = extensionPoint;
|
||||
this.uniqueIdentify = this.businessScenario.getUniqueIdentity();
|
||||
this.extensionPointClass = this.extensionPoint.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建definition
|
||||
* @param businessScenario
|
||||
* @param point
|
||||
* @return
|
||||
*/
|
||||
public static ExtensionDefinition valueOf(@NotNull BusinessScenario businessScenario, @NotNull ExtensionPoint point) {
|
||||
return new ExtensionDefinition(businessScenario, point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ExtensionDefinition that = (ExtensionDefinition) o;
|
||||
return Objects.equals(uniqueIdentify, that.uniqueIdentify) && Objects.equals(extensionPointName, that.extensionPointName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((uniqueIdentify == null) ? 0 : uniqueIdentify.hashCode());
|
||||
result = prime * result + ((extensionPointName == null) ? 0 : extensionPointName.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExtensionDefinition{" +
|
||||
"uniqueIdentify='" + uniqueIdentify + '\'' +
|
||||
", extensionPointName='" + extensionPointName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.factory;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* @description 扩展点工厂
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 23:04
|
||||
* @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionFactory.java
|
||||
*/
|
||||
public interface ExtensionFactory {
|
||||
|
||||
/**
|
||||
* 注册所有扩展点实现类
|
||||
* @param basePackage
|
||||
*/
|
||||
void register(String basePackage);
|
||||
|
||||
/**
|
||||
* 根据业务场景获取指定类型的扩展点
|
||||
* @param businessScenario
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T extends ExtensionPoint> T get(BusinessScenario businessScenario, Class<T> clazz);
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.factory;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
import cn.iocoder.yudao.framework.extension.core.stereotype.Extension;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @description 注册工厂
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 23:07
|
||||
* @class cn.iocoder.yudao.framework.extension.core.factory.ExtensionRegisterFactory.java
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ExtensionRegisterFactory implements ExtensionFactory {
|
||||
|
||||
/**
|
||||
* spring ApplicationContext
|
||||
*/
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 扩展点实现类集合
|
||||
*/
|
||||
private Map<String, ExtensionDefinition> registerExtensionBeans = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void register(String basePackage) {
|
||||
final Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Extension.class);
|
||||
if(beans == null || beans.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
beans.values().forEach(point -> doRegister((ExtensionPoint) point));
|
||||
log.info("业务场景相关扩展点注册完成,注册数量: {}", registerExtensionBeans.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ExtensionPoint> T get(BusinessScenario businessScenario, Class<T> clazz) {
|
||||
|
||||
final ExtensionDefinition definition = registerExtensionBeans.get(businessScenario.getUniqueIdentity());
|
||||
if(definition == null) {
|
||||
log.error("获取业务场景扩展点实现失败,失败原因:尚未定义该业务场景相关扩展点。{}", businessScenario);
|
||||
throw new RuntimeException("尚未定义该业务场景相关扩展点 [" + businessScenario + "]");
|
||||
}
|
||||
|
||||
return (T) definition.getExtensionPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册扩展点
|
||||
* @param point
|
||||
*/
|
||||
private void doRegister(@NotNull ExtensionPoint point) {
|
||||
Class<?> extensionClazz = point.getClass();
|
||||
|
||||
if (AopUtils.isAopProxy(point)) {
|
||||
extensionClazz = ClassUtils.getUserClass(point);
|
||||
}
|
||||
|
||||
Extension extension = AnnotationUtils.findAnnotation(extensionClazz, Extension.class);
|
||||
final BusinessScenario businessScenario = BusinessScenario.valueOf(extension.businessId(), extension.useCase(), extension.scenario());
|
||||
final ExtensionDefinition definition = ExtensionDefinition.valueOf(businessScenario, point);
|
||||
final ExtensionDefinition exist = registerExtensionBeans.get(businessScenario.getUniqueIdentity());
|
||||
if(exist != null && !exist.equals(definition)) {
|
||||
throw new RuntimeException("相同的业务场景重复注册了不同类型的扩展点实现 :【" + definition + "】【" + exist + "】");
|
||||
}
|
||||
|
||||
registerExtensionBeans.put(businessScenario.getUniqueIdentity(), definition);
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @description core 核心
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 21:54
|
||||
* @class cn.iocoder.yudao.framework.extension.core.package-info.java
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.extension.core;
|
@ -1,11 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.point;
|
||||
/**
|
||||
* @description 扩展点 <br/>
|
||||
* 表示一块逻辑在不同的业务有不同的实现,使用扩展点做接口申明,然后用{@linkplain cn.iocoder.yudao.framework.extension.core.stereotype.Extension}(扩展)去实现扩展点。
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 22:06
|
||||
* @class cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint.java
|
||||
*/
|
||||
public interface ExtensionPoint {
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.core.stereotype;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @description 表示带注释的类是“扩展组件”
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 21:59
|
||||
* @class cn.iocoder.yudao.framework.extension.core.stereotype.Extension.java
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Component
|
||||
public @interface Extension {
|
||||
|
||||
/**
|
||||
* 业务 <br/>
|
||||
* 一个自负盈亏的财务主体,比如tmall、淘宝和零售通就是三个不同的业务
|
||||
* @return
|
||||
*/
|
||||
String businessId() default BusinessScenario.DEFAULT_BUSINESS_ID;
|
||||
|
||||
/**
|
||||
* 用例 <br/>
|
||||
* 描述了用户和系统之间的互动,每个用例提供了一个或多个场景。比如,支付订单就是一个典型的用例。
|
||||
* @return
|
||||
*/
|
||||
String useCase() default BusinessScenario.DEFAULT_USECASE;
|
||||
|
||||
/**
|
||||
* 场景 <br/>
|
||||
* 场景也被称为用例的实例(Instance),包括用例所有的可能情况(正常的和异常的)。比如对于"订单支付"这个用例,就有“支付宝支付”、“银行卡支付”、"微信支付"等多个场景
|
||||
* @return
|
||||
*/
|
||||
String scenario() default BusinessScenario.DEFAULT_SCENARIO;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @description 扩展点组件
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-28 14:35
|
||||
* @class cn.iocoder.yudao.framework.extension.package-info.java
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.extension;
|
@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.yudao.framework.extension.config.YudaoExtensionAutoConfiguration
|
@ -1,19 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @description Application
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:32
|
||||
* @class cn.iocoder.yudao.framework.extension.Application.java
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.yudao.framework.extension.core.BusinessScenario;
|
||||
import cn.iocoder.yudao.framework.extension.core.context.ExtensionExecutor;
|
||||
import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint;
|
||||
import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
|
||||
import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:30
|
||||
* @class cn.iocoder.yudao.framework.extension.ExtensionTest.java
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class)
|
||||
@Slf4j
|
||||
public class ExtensionTest {
|
||||
|
||||
@Autowired
|
||||
private ExtensionExecutor extensionExecutor;
|
||||
|
||||
@Test
|
||||
public void unifiedOrder() {
|
||||
final BusinessScenario scenario = BusinessScenario.valueOf("pay", "jsapi", "wechat");
|
||||
final TransactionsCommand command = new TransactionsCommand(IdUtil.objectId(), new BigDecimal(105), "Image形象店-深圳腾大-QQ公仔", "https://www.weixin.qq.com/wxpay/pay.php");
|
||||
final TransactionsResult result = extensionExecutor.execute(PayExtensionPoint.class, scenario, extension -> extension.unifiedOrder(command));
|
||||
log.info("result is: {}", JSONUtil.toJsonStr(result));
|
||||
Assert.assertSame("wechat", result.getChannel());
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @description
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:25
|
||||
* @class cn.iocoder.yudao.framework.extension.package-info.java
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.extension;
|
@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.pay;
|
||||
|
||||
import cn.iocoder.yudao.framework.extension.core.point.ExtensionPoint;
|
||||
import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
|
||||
import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
|
||||
|
||||
/**
|
||||
* @description 支付操作接口
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:35
|
||||
* @class cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint.java
|
||||
*/
|
||||
public interface PayExtensionPoint extends ExtensionPoint {
|
||||
|
||||
/**
|
||||
* 统一下单:获取"预支付交易会话标识"
|
||||
* @param command
|
||||
* @return
|
||||
*/
|
||||
TransactionsResult unifiedOrder(TransactionsCommand command);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.pay.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @description 下单请求
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:48
|
||||
* @class cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand.java
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TransactionsCommand implements Serializable {
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 商品描述
|
||||
*/
|
||||
private String productDescription;
|
||||
|
||||
/**
|
||||
* 通知地址
|
||||
*/
|
||||
private String notifyUrl;
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.pay.domain;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @description 下单: 预支付交易单返回结果
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:43
|
||||
* @class cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult.java
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class TransactionsResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 预支付交易会话标识
|
||||
*/
|
||||
private String prepayId;
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 系统内部支付单号
|
||||
*/
|
||||
private String paymentNo;
|
||||
|
||||
/**
|
||||
* 支付渠道:微信 or 支付宝
|
||||
*/
|
||||
private String channel;
|
||||
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.pay.impl;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.yudao.framework.extension.core.stereotype.Extension;
|
||||
import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint;
|
||||
import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
|
||||
import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @description 微信 JSAPI 支付
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:38
|
||||
* @class cn.iocoder.yudao.framework.extension.pay.impl.AlipayService.java
|
||||
*/
|
||||
@Extension(businessId = "pay", useCase = "jsapi", scenario = "alipay")
|
||||
@Slf4j
|
||||
public class AlipayService implements PayExtensionPoint {
|
||||
@Override
|
||||
public TransactionsResult unifiedOrder(TransactionsCommand command) {
|
||||
log.info("微信 JSAPI 支付:{}", JSONUtil.toJsonStr(command));
|
||||
return new TransactionsResult("alipay26112221580621e9b071c00d9e093b0000", command.getOrderNo(), IdUtil.objectId(), "alipay");
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.extension.pay.impl;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.yudao.framework.extension.core.stereotype.Extension;
|
||||
import cn.iocoder.yudao.framework.extension.pay.PayExtensionPoint;
|
||||
import cn.iocoder.yudao.framework.extension.pay.command.TransactionsCommand;
|
||||
import cn.iocoder.yudao.framework.extension.pay.domain.TransactionsResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @description 微信 JSAPI 支付
|
||||
* @author Qingchen
|
||||
* @version 1.0.0
|
||||
* @date 2021-08-30 10:37
|
||||
* @class cn.iocoder.yudao.framework.extension.pay.impl.WechatPayService.java
|
||||
*/
|
||||
@Extension(businessId = "pay", useCase = "jsapi", scenario = "wechat")
|
||||
@Slf4j
|
||||
public class WechatPayService implements PayExtensionPoint {
|
||||
@Override
|
||||
public TransactionsResult unifiedOrder(TransactionsCommand command) {
|
||||
log.info("微信 JSAPI 支付:{}", JSONUtil.toJsonStr(command));
|
||||
return new TransactionsResult("wx26112221580621e9b071c00d9e093b0000", command.getOrderNo(), IdUtil.objectId(), "wechat");
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
### 作用
|
||||
|
||||
为了解决同一个流程不同业务有不同处理逻辑而产生,减少代码中 if else 逻辑,降低代码的耦合性,通过统一的扩展形式来支撑业务的变化。
|
||||
|
||||
### 原理
|
||||
|
||||
https://blog.csdn.net/significantfrank/article/details/100074716
|
||||
|
||||
|
||||
|
||||
### 使用介绍
|
||||
|
||||
参考测试代码 `cn.iocoder.yudao.framework.extension.ExtensionTest`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -53,6 +53,13 @@
|
||||
<scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-infra-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 服务保障相关 -->
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
|
@ -2,9 +2,14 @@ package cn.iocoder.yudao.framework.apilog.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkServiceImpl;
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration;
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
@ -17,6 +22,16 @@ import javax.servlet.Filter;
|
||||
@AutoConfigureAfter(YudaoWebAutoConfiguration.class)
|
||||
public class YudaoApiLogAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) {
|
||||
return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) {
|
||||
return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 ApiAccessLogFilter Bean,记录 API 请求日志
|
||||
*/
|
||||
|
@ -3,8 +3,8 @@ package cn.iocoder.yudao.framework.apilog.core.filter;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiAccessLogCreateReqDTO;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
@ -66,16 +66,16 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
|
||||
|
||||
private void createApiAccessLog(HttpServletRequest request, Date beginTime,
|
||||
Map<String, String> queryString, String requestBody, Exception ex) {
|
||||
ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO();
|
||||
ApiAccessLog accessLog = new ApiAccessLog();
|
||||
try {
|
||||
this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex);
|
||||
apiAccessLogFrameworkService.createApiAccessLogAsync(accessLog);
|
||||
apiAccessLogFrameworkService.createApiAccessLog(accessLog);
|
||||
} catch (Throwable th) {
|
||||
log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildApiAccessLogDTO(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, Date beginTime,
|
||||
private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, Date beginTime,
|
||||
Map<String, String> queryString, String requestBody, Exception ex) {
|
||||
// 处理用户信息
|
||||
accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request));
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.apilog.core.service.dto;
|
||||
package cn.iocoder.yudao.framework.apilog.core.service;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ -6,12 +6,12 @@ import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* API 访问日志创建 DTO
|
||||
* API 访问日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class ApiAccessLogCreateReqDTO {
|
||||
public class ApiAccessLog {
|
||||
|
||||
/**
|
||||
* 链路追踪编号
|
@ -1,9 +1,5 @@
|
||||
package cn.iocoder.yudao.framework.apilog.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiAccessLogCreateReqDTO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* API 访问日志 Framework Service 接口
|
||||
*
|
||||
@ -14,8 +10,8 @@ public interface ApiAccessLogFrameworkService {
|
||||
/**
|
||||
* 创建 API 访问日志
|
||||
*
|
||||
* @param createDTO 创建信息
|
||||
* @param apiAccessLog API 访问日志
|
||||
*/
|
||||
void createApiAccessLogAsync(@Valid ApiAccessLogCreateReqDTO createDTO);
|
||||
void createApiAccessLog(ApiAccessLog apiAccessLog);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.framework.apilog.core.service;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
/**
|
||||
* API 访问日志 Framework Service 实现类
|
||||
*
|
||||
* 基于 {@link ApiAccessLogApi} 服务,记录访问日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService {
|
||||
|
||||
private final ApiAccessLogApi apiAccessLogApi;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void createApiAccessLog(ApiAccessLog apiAccessLog) {
|
||||
ApiAccessLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiAccessLog, ApiAccessLogCreateReqDTO.class);
|
||||
apiAccessLogApi.createApiAccessLog(reqDTO);
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,17 @@
|
||||
package cn.iocoder.yudao.framework.apilog.core.service.dto;
|
||||
package cn.iocoder.yudao.framework.apilog.core.service;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* API 错误日志创建 DTO
|
||||
* API 错误日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ApiErrorLogCreateReqDTO {
|
||||
public class ApiErrorLog {
|
||||
|
||||
/**
|
||||
* 链路编号
|
||||
@ -105,4 +103,5 @@ public class ApiErrorLogCreateReqDTO {
|
||||
@NotNull(message = "异常导致的消息不能为空")
|
||||
private String exceptionMessage;
|
||||
|
||||
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
package cn.iocoder.yudao.framework.apilog.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiErrorLogCreateReqDTO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* API 错误日志 Framework Service 接口
|
||||
*
|
||||
@ -14,8 +10,8 @@ public interface ApiErrorLogFrameworkService {
|
||||
/**
|
||||
* 创建 API 错误日志
|
||||
*
|
||||
* @param createDTO 创建信息
|
||||
* @param apiErrorLog API 错误日志
|
||||
*/
|
||||
void createApiErrorLogAsync(@Valid ApiErrorLogCreateReqDTO createDTO);
|
||||
void createApiErrorLog(ApiErrorLog apiErrorLog);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.framework.apilog.core.service;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
/**
|
||||
* API 错误日志 Framework Service 实现类
|
||||
*
|
||||
* 基于 {@link ApiErrorLogApi} 服务,记录错误日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService {
|
||||
|
||||
private final ApiErrorLogApi apiErrorLogApi;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void createApiErrorLog(ApiErrorLog apiErrorLog) {
|
||||
ApiErrorLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiErrorLog, ApiErrorLogCreateReqDTO.class);
|
||||
apiErrorLogApi.createApiErrorLog(reqDTO);
|
||||
}
|
||||
|
||||
}
|
@ -3,14 +3,14 @@ package cn.iocoder.yudao.framework.web.core.handler;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLog;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.dto.ApiErrorLogCreateReqDTO;
|
||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -229,18 +229,18 @@ public class GlobalExceptionHandler {
|
||||
|
||||
private void createExceptionLog(HttpServletRequest req, Throwable e) {
|
||||
// 插入错误日志
|
||||
ApiErrorLogCreateReqDTO errorLog = new ApiErrorLogCreateReqDTO();
|
||||
ApiErrorLog errorLog = new ApiErrorLog();
|
||||
try {
|
||||
// 初始化 errorLog
|
||||
initExceptionLog(errorLog, req, e);
|
||||
// 执行插入 errorLog
|
||||
apiErrorLogFrameworkService.createApiErrorLogAsync(errorLog);
|
||||
apiErrorLogFrameworkService.createApiErrorLog(errorLog);
|
||||
} catch (Throwable th) {
|
||||
log.error("[createExceptionLog][url({}) log({}) 发生异常]", req.getRequestURI(), JsonUtils.toJsonString(errorLog), th);
|
||||
}
|
||||
}
|
||||
|
||||
private void initExceptionLog(ApiErrorLogCreateReqDTO errorLog, HttpServletRequest request, Throwable e) {
|
||||
private void initExceptionLog(ApiErrorLog errorLog, HttpServletRequest request, Throwable e) {
|
||||
// 处理用户信息
|
||||
errorLog.setUserId(WebFrameworkUtils.getLoginUserId(request));
|
||||
errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request));
|
||||
|
Reference in New Issue
Block a user