@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationRecordLog {
String value() default "";
String expression() default "";
String goodsIdsExpression() default "";
String operationSpecificId() default "";
String goodsIdsAndOperationSpecificId() default "";
}
* 仓储管理操作记录日志使用spring event异步入库
* @Author yzj
* @Description
* @Date 2025/01/02/9:44
* @Version 1.0
*/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class OperationRecordLogAspect {
private final ApplicationEventPublisher publisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
@SneakyThrows
@Around("@annotation(operationRecordLog)")
public Object around(ProceedingJoinPoint point, OperationRecordLog operationRecordLog) {
String strClassName = point.getTarget().getClass().getName();
String strMethodName = point.getSignature().getName();
String goodsIds = operationRecordLog.goodsIdsExpression();
String operationSpecificId = operationRecordLog.operationSpecificId();
String goodsIdsAndOperationSpecificId = operationRecordLog.goodsIdsAndOperationSpecificId();
log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
String value = operationRecordLog.value();
String expression = operationRecordLog.expression();
MethodSignature signature = (MethodSignature) point.getSignature();
EvaluationContext context = SysLogUtils.getContext(point.getArgs(), signature.getMethod());
if (StrUtil.isNotBlank(expression)) {
try {
value = SysLogUtils.getValue(context, expression, String.class);
} catch (Exception e) {
log.error("@SysLog 解析SPEL {} 异常", expression);
}
}
List<Long> parsedGoodsIds = new ArrayList<>();
List<Long> parsedOperationSpecificId = new ArrayList<>();
Map<Long, List<Long>> operationSpecificIdToGoodsIdsMap = new HashMap<>();
SysLogDTO logDTO = SysLogUtils.getSysLog();
OperationRecordLogDto operationRecordLogDto = new OperationRecordLogDto();
BeanUtils.copyProperties(logDTO, operationRecordLogDto);
Object[] args = point.getArgs();
String paramJson = paramToJson(args);
operationRecordLogDto.setParams(logDTO.getParams() + paramJson);
operationRecordLogDto.setTitle(value);
Long startTime = System.currentTimeMillis();
Object obj;
try {
obj = point.proceed();
if (StringUtils.isNotBlank(goodsIds)) {
try {
parsedGoodsIds = SysLogUtils.getValue(context, goodsIds, List.class);
} catch (Exception e) {
log.error("解析 goodsIds SPEL 表达式出错", e);
}
}
if (StrUtil.isNotBlank(operationSpecificId)) {
try {
parsedOperationSpecificId = SysLogUtils.getValue(context, operationSpecificId, List.class);
} catch (Exception e) {
log.error("解析 operationSpecificId SPEL 表达式出错", e);
}
}
if(StringUtils.isNotBlank(goodsIdsAndOperationSpecificId)){
try {
operationSpecificIdToGoodsIdsMap = SysLogUtils.getValue(context, goodsIdsAndOperationSpecificId, Map.class);
} catch (Exception e) {
log.error("解析 goodsIdsAndOperationSpecificId SPEL 表达式出错", e);
}
}
} catch (Exception e) {
operationRecordLogDto.setLogType(LogTypeEnum.ERROR.getType());
operationRecordLogDto.setException(e.getMessage());
throw e;
} finally {
Long endTime = System.currentTimeMillis();
operationRecordLogDto.setTime(endTime - startTime);
if(CollUtil.isNotEmpty(parsedOperationSpecificId)&&parsedOperationSpecificId.size()==1){
operationRecordLogDto.setOperationSpecificId(parsedOperationSpecificId.get(0));
if(CollUtil.isNotEmpty(operationSpecificIdToGoodsIdsMap)){
List<Long> ids = operationSpecificIdToGoodsIdsMap.get(parsedOperationSpecificId.get(0));
publishLogEvents(operationRecordLogDto, ids);
}else {
publishLogEvents(operationRecordLogDto, parsedGoodsIds);
}
}else if(CollUtil.isNotEmpty(parsedOperationSpecificId)&&parsedOperationSpecificId.size()>1){
for (Map.Entry<Long, List<Long>> entry : operationSpecificIdToGoodsIdsMap.entrySet()) {
Long SpecificId = entry.getKey();
List<Long> ids = entry.getValue();
operationRecordLogDto.setOperationSpecificId(SpecificId);
publishLogEvents(operationRecordLogDto, ids);
}
}
}
return obj;
}
private void publishLogEvents(OperationRecordLogDto baseDto, List<Long> goodsIds) {
if (CollUtil.isEmpty(goodsIds)) {
publisher.publishEvent(new OperationRecordLogEvent(baseDto));
} else {
for (Long goodsId : goodsIds) {
OperationRecordLogDto dto = new OperationRecordLogDto();
BeanUtils.copyProperties(baseDto, dto);
dto.setGoodsId(goodsId);
publisher.publishEvent(new OperationRecordLogEvent(dto));
}
}
}
private String paramToJson(Object[] args) {
StringBuilder paramJson = new StringBuilder();
if (ArrayUtil.isNotEmpty(args)) {
paramJson.append("[");
for (Object entity : args) {
try {
paramJson.append(objectMapper.writeValueAsString(entity))
.append(",\n");
} catch (JsonProcessingException e) {
try {
paramJson.append(JSONObject.toJSONString(entity))
.append(",\n");
} catch (Exception ex) {
paramJson.append(entity);
}
}
}
if (paramJson.length() > 1) {
paramJson.deleteCharAt(paramJson.length() - 1);
}
paramJson.append("]");
}
return paramJson.toString();
}
}
@Data
@Schema(description = "仓储管理操作记录日志查询对象")
public class OperationRecordLogDto {
private Long operationId;
@NotBlank(message = "日志类型不能为空")
private String logType;
@NotBlank(message = "日志标题不能为空")
private String title;
private Long goodsId;
private Long operationSpecificId;
private String createBy;
private LocalDateTime createTime;
private String remoteAddr;
private String userAgent;
private String requestUri;
private String method;
private String params;
private Long time;
private String exception;
}
@Getter
@AllArgsConstructor
public class OperationRecordLogEvent {
private final OperationRecordLogDto operationRecord;
}
@Slf4j
@AllArgsConstructor
@Component
public class OperationRecordLogListener {
private final IOperationRecordService operationRecordService;
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(OperationRecordLogEvent event) {
OperationRecordLogDto operationRecordLog = event.getOperationRecord();
operationRecordService.saveLog(operationRecordLog);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗