/**
* 仓储管理操作记录日志注解
* @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);
}
}