仓储管理操作记录日志使用spring event异步入库

/**
 * 仓储管理操作记录日志注解
 * @Author yzj
 * @Description
 * @Date 2025/01/02/9:41
 * @Version 1.0
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationRecordLog {
    /**
     * 描述
     * @return {String}
     */
    String value() default "";

    /**
     * spel 表达式
     * @return 日志描述
     */
    String expression() default "";

    /**
     * 货品id列表
     * @return
     */
    String goodsIdsExpression() default "";

    /**
     * 操作具体ID
     * @return
     */
    String operationSpecificId() default "";
    /**
     * 操作具体ID与货品ID列表的映射(用来对应关系)
     * 例如批量删除入库信息 一个入库信息对应多个货品信息
     * @return
     */
    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();

    /**
     * 仓储管理操作记录日志使用spring event异步入库
     * @param operationRecordLog
     */
    @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());
        // 当前表达式存在 SPEL,会覆盖 value 的值
        if (StrUtil.isNotBlank(expression)) {
            // 解析SPEL
            try {
                value = SysLogUtils.getValue(context, expression, String.class);
            } catch (Exception e) {
                // SPEL 表达式异常,则获取 value 的值
                log.error("@SysLog 解析SPEL {} 异常", expression);
            }
        }

        // 解析 goodsIds 的 SPEL 表达式
        List<Long> parsedGoodsIds = new ArrayList<>();

        // 解析 operationSpecificId 的 SPEL 表达式
        List<Long> parsedOperationSpecificId = new ArrayList<>();


        //解析 goodsIdsAndOperationSpecificId 的 SPEL 表达式
        Map<Long, List<Long>> operationSpecificIdToGoodsIdsMap  = new HashMap<>();

        SysLogDTO logDTO = SysLogUtils.getSysLog();
        OperationRecordLogDto operationRecordLogDto = new OperationRecordLogDto();
        BeanUtils.copyProperties(logDTO, operationRecordLogDto);
        //body体中存在参数 需要额外添加
        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();
                    //货品id列表
                    List<Long> ids = entry.getValue();
                    //设置具体的操作id
                    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)) {
            // 处理多个请求实体, 转换为 数据1,数据2 json格式
            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();
    }
}
/**
 * 仓储管理操作记录日志查询传输对象
 * @Author yzj
 * @Description
 * @Date 2025/01/02/9:53
 * @Version 1.0
 */
@Data
@Schema(description = "仓储管理操作记录日志查询对象")
public class OperationRecordLogDto {
    /**
     * 操作记录ID
     */
    private Long operationId;
    /**
     * 日志类型
     */
    @NotBlank(message = "日志类型不能为空")
    private String logType;
    /**
     * 日志标题
     */
    @NotBlank(message = "日志标题不能为空")
    private String title;
    /**
     * 货品ID
     */
    private Long goodsId;
    /**
     * 操作具体ID
     */
    private Long operationSpecificId;
    /**
     * 操作人
     */
    private String createBy;
    /**
     * 操作时间
     */
    private LocalDateTime createTime;
    /**
     * 操作IP地址
     */
    private String remoteAddr;
    /**
     * 用户代理
     */
    private String userAgent;
    /**
     * 请求URI
     */
    private String requestUri;
    /**
     * 操作方式
     */
    private String method;
    /**
     * 操作提交的数据
     */
    private String params;
    /**
     * 执行时间
     */
    private Long time;
    /**
     * 异常信息
     */
    private String exception;

}
@Getter
@AllArgsConstructor
public class OperationRecordLogEvent {
    private final OperationRecordLogDto operationRecord;
}
/**
 * 异步监听仓储管理操作记录日志事件
 * @Author yzj
 * @Description
 * @Date 2025/01/02/10:14
 * @Version 1.0
 */
@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);
    }
}
posted @   狗狗没有坏心眼  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
点击右上角即可分享
微信分享提示