|
@@ -0,0 +1,291 @@
|
|
|
|
|
+package shop.alien.storeplatform.aspect;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
|
|
+import org.aspectj.lang.annotation.Around;
|
|
|
|
|
+import org.aspectj.lang.annotation.Aspect;
|
|
|
|
|
+import org.aspectj.lang.annotation.Pointcut;
|
|
|
|
|
+import org.aspectj.lang.reflect.MethodSignature;
|
|
|
|
|
+import org.springframework.core.DefaultParameterNameDiscoverer;
|
|
|
|
|
+import org.springframework.core.annotation.Order;
|
|
|
|
|
+import org.springframework.expression.EvaluationContext;
|
|
|
|
|
+import org.springframework.expression.Expression;
|
|
|
|
|
+import org.springframework.expression.common.TemplateParserContext;
|
|
|
|
|
+import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|
|
|
|
+import org.springframework.expression.spel.support.StandardEvaluationContext;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
|
+import org.springframework.web.context.request.RequestContextHolder;
|
|
|
|
|
+import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
|
|
+import shop.alien.entity.result.R;
|
|
|
|
|
+import shop.alien.entity.store.StorePlatformMenu;
|
|
|
|
|
+import shop.alien.entity.store.StorePlatformRole;
|
|
|
|
|
+import shop.alien.entity.storePlatform.StorePlatformOperationLog;
|
|
|
|
|
+import shop.alien.mapper.StorePlatformMenuMapper;
|
|
|
|
|
+import shop.alien.storeplatform.annotation.PlatformOperationLog;
|
|
|
|
|
+import shop.alien.storeplatform.dto.CreateRoleDto;
|
|
|
|
|
+import shop.alien.storeplatform.dto.UpdateRoleDto;
|
|
|
|
|
+import shop.alien.storeplatform.service.PlatformOperationLogService;
|
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
+import shop.alien.storeplatform.service.StorePlatformRoleMenuService;
|
|
|
|
|
+import shop.alien.storeplatform.service.StorePlatformRoleService;
|
|
|
|
|
+import shop.alien.storeplatform.util.LoginUserUtil;
|
|
|
|
|
+
|
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 平台操作记录切面
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author system
|
|
|
|
|
+ * @since 2025-01-XX
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Aspect
|
|
|
|
|
+@Component
|
|
|
|
|
+@Order(10)
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class PlatformOperationLogAspect {
|
|
|
|
|
+
|
|
|
|
|
+ private final PlatformOperationLogService platformOperationLogService;
|
|
|
|
|
+ private final StorePlatformRoleService storePlatformRoleService;
|
|
|
|
|
+ private final StorePlatformRoleMenuService storePlatformRoleMenuService;
|
|
|
|
|
+ private final StorePlatformMenuMapper storePlatformMenuMapper;
|
|
|
|
|
+ private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
|
|
|
|
|
+ private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
|
|
|
|
+ private final TemplateParserContext templateParserContext = new TemplateParserContext("#{", "}");
|
|
|
|
|
+
|
|
|
|
|
+ @Pointcut("@annotation(shop.alien.storeplatform.annotation.PlatformOperationLog)")
|
|
|
|
|
+ public void operationLogPointcut() {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Around("operationLogPointcut()")
|
|
|
|
|
+ public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
|
|
|
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
|
|
|
|
+ Method method = signature.getMethod();
|
|
|
|
|
+ PlatformOperationLog annotation = method.getAnnotation(PlatformOperationLog.class);
|
|
|
|
|
+
|
|
|
|
|
+ Object result;
|
|
|
|
|
+ try {
|
|
|
|
|
+ result = joinPoint.proceed();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ throw e;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (annotation != null && shouldRecord(result)) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ StorePlatformOperationLog logRecord = buildLogRecord(joinPoint, annotation, result);
|
|
|
|
|
+ platformOperationLogService.save(logRecord);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("平台操作记录失败: {}", e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean shouldRecord(Object result) {
|
|
|
|
|
+ if (result instanceof R) {
|
|
|
|
|
+ return R.isSuccess((R<?>) result);
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private StorePlatformOperationLog buildLogRecord(ProceedingJoinPoint joinPoint, PlatformOperationLog annotation, Object result) {
|
|
|
|
|
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
|
|
|
|
+ Method method = signature.getMethod();
|
|
|
|
|
+ Object[] args = joinPoint.getArgs();
|
|
|
|
|
+ String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
|
|
|
|
|
+
|
|
|
|
|
+ EvaluationContext context = new StandardEvaluationContext();
|
|
|
|
|
+ if (args != null) {
|
|
|
|
|
+ for (int i = 0; i < args.length; i++) {
|
|
|
|
|
+ context.setVariable("p" + i, args[i]);
|
|
|
|
|
+ context.setVariable("arg" + i, args[i]);
|
|
|
|
|
+ if (parameterNames != null && parameterNames.length > i) {
|
|
|
|
|
+ context.setVariable(parameterNames[i], args[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ context.setVariable("result", result);
|
|
|
|
|
+
|
|
|
|
|
+ StorePlatformOperationLog logRecord = new StorePlatformOperationLog();
|
|
|
|
|
+ logRecord.setOperationModule(annotation.module());
|
|
|
|
|
+ logRecord.setOperationType(annotation.type());
|
|
|
|
|
+ logRecord.setOperationTime(new Date());
|
|
|
|
|
+
|
|
|
|
|
+ String content = resolveContent(annotation, context, args);
|
|
|
|
|
+ logRecord.setOperationContent(content);
|
|
|
|
|
+ logRecord.setOperationParams(buildParamsJson(parameterNames, args));
|
|
|
|
|
+
|
|
|
|
|
+ fillOperatorInfo(logRecord);
|
|
|
|
|
+ fillRequestInfo(logRecord);
|
|
|
|
|
+ return logRecord;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void fillOperatorInfo(StorePlatformOperationLog logRecord) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ JSONObject userInfo = LoginUserUtil.getCurrentUserInfo();
|
|
|
|
|
+ if (userInfo != null) {
|
|
|
|
|
+ logRecord.setOperatorId(userInfo.getString("userId"));
|
|
|
|
|
+ String account = userInfo.getString("phone");
|
|
|
|
|
+ if (!StringUtils.hasText(account)) {
|
|
|
|
|
+ account = userInfo.getString("account");
|
|
|
|
|
+ }
|
|
|
|
|
+ logRecord.setOperatorAccount(account);
|
|
|
|
|
+ String name = userInfo.getString("userName");
|
|
|
|
|
+ if (!StringUtils.hasText(name)) {
|
|
|
|
|
+ name = userInfo.getString("name");
|
|
|
|
|
+ }
|
|
|
|
|
+ logRecord.setOperatorName(name);
|
|
|
|
|
+ String userType = userInfo.getString("userType");
|
|
|
|
|
+ logRecord.setUserType(StringUtils.hasText(userType) ? userType : "platform");
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.debug("填充操作人信息失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void fillRequestInfo(StorePlatformOperationLog logRecord) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
|
|
+ if (attributes == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ HttpServletRequest request = attributes.getRequest();
|
|
|
|
|
+ logRecord.setRequestMethod(request.getMethod());
|
|
|
|
|
+ logRecord.setRequestPath(request.getRequestURI());
|
|
|
|
|
+ logRecord.setIpAddress(getIpAddress(request));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.debug("填充请求信息失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String getIpAddress(HttpServletRequest request) {
|
|
|
|
|
+ String ip = request.getHeader("X-Forwarded-For");
|
|
|
|
|
+ if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
|
+ ip = request.getHeader("Proxy-Client-IP");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
|
+ ip = request.getHeader("WL-Proxy-Client-IP");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
|
+ ip = request.getHeader("HTTP_CLIENT_IP");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
|
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!StringUtils.hasText(ip) || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
|
+ ip = request.getRemoteAddr();
|
|
|
|
|
+ }
|
|
|
|
|
+ return ip;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String resolveContent(PlatformOperationLog annotation, EvaluationContext context, Object[] args) {
|
|
|
|
|
+ if ("角色操作记录".equals(annotation.module())) {
|
|
|
|
|
+ return buildRoleOperationContent(annotation.type(), args);
|
|
|
|
|
+ }
|
|
|
|
|
+ String content = annotation.content();
|
|
|
|
|
+ if (StringUtils.hasText(content)) {
|
|
|
|
|
+ Expression exp = spelExpressionParser.parseExpression(content, templateParserContext);
|
|
|
|
|
+ String parsed = exp.getValue(context, String.class);
|
|
|
|
|
+ return parsed != null ? parsed : annotation.type();
|
|
|
|
|
+ }
|
|
|
|
|
+ return annotation.type();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String buildParamsJson(String[] parameterNames, Object[] args) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (args == null || args.length == 0) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (parameterNames == null || parameterNames.length == 0) {
|
|
|
|
|
+ return JSON.toJSONString(args);
|
|
|
|
|
+ }
|
|
|
|
|
+ java.util.Map<String, Object> params = new java.util.LinkedHashMap<>();
|
|
|
|
|
+ for (int i = 0; i < args.length; i++) {
|
|
|
|
|
+ String name = parameterNames.length > i ? parameterNames[i] : "arg" + i;
|
|
|
|
|
+ params.put(name, args[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ return JSON.toJSONString(params);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.debug("序列化操作入参失败", e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String buildRoleOperationContent(String operationType, Object[] args) {
|
|
|
|
|
+ String roleName = null;
|
|
|
|
|
+ List<Long> menuIds = Collections.emptyList();
|
|
|
|
|
+ Long roleId = null;
|
|
|
|
|
+
|
|
|
|
|
+ if (args != null && args.length > 0) {
|
|
|
|
|
+ Object firstArg = args[0];
|
|
|
|
|
+ if (firstArg instanceof StorePlatformRole) {
|
|
|
|
|
+ StorePlatformRole role = (StorePlatformRole) firstArg;
|
|
|
|
|
+ roleName = role.getRoleName();
|
|
|
|
|
+ } else if (firstArg instanceof CreateRoleDto) {
|
|
|
|
|
+ CreateRoleDto dto = (CreateRoleDto) firstArg;
|
|
|
|
|
+ roleName = dto.getRoleName();
|
|
|
|
|
+ menuIds = dto.getMenuIds() != null ? dto.getMenuIds() : Collections.emptyList();
|
|
|
|
|
+ } else if (firstArg instanceof UpdateRoleDto) {
|
|
|
|
|
+ UpdateRoleDto dto = (UpdateRoleDto) firstArg;
|
|
|
|
|
+ roleName = dto.getRoleName();
|
|
|
|
|
+ roleId = dto.getRoleId();
|
|
|
|
|
+ menuIds = dto.getMenuIds() != null ? dto.getMenuIds() : Collections.emptyList();
|
|
|
|
|
+ } else if (firstArg instanceof Long) {
|
|
|
|
|
+ roleId = (Long) firstArg;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ((roleName == null || roleName.trim().isEmpty()) && roleId != null) {
|
|
|
|
|
+ StorePlatformRole role = storePlatformRoleService.getRoleById(roleId);
|
|
|
|
|
+ if (role != null) {
|
|
|
|
|
+ roleName = role.getRoleName();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ((menuIds == null || menuIds.isEmpty()) && roleId != null) {
|
|
|
|
|
+ menuIds = storePlatformRoleMenuService.getMenuIdsByRoleId(roleId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<String> menuNames = resolveMenuNames(menuIds);
|
|
|
|
|
+ int count = menuNames.size();
|
|
|
|
|
+ String menuSummary = menuNames.isEmpty() ? "无权限" : menuNames.get(0);
|
|
|
|
|
+ String roleLabel = StringUtils.hasText(roleName) ? roleName : "未知";
|
|
|
|
|
+ String type = normalizeRoleOperationType(operationType);
|
|
|
|
|
+ return String.format("%s%s角色标签,包含%s等%d个权限", type, roleLabel, menuSummary, count);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<String> resolveMenuNames(List<Long> menuIds) {
|
|
|
|
|
+ if (menuIds == null || menuIds.isEmpty()) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ return storePlatformMenuMapper.selectBatchIds(menuIds).stream()
|
|
|
|
|
+ .map(StorePlatformMenu::getMenuName)
|
|
|
|
|
+ .filter(StringUtils::hasText)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String normalizeRoleOperationType(String operationType) {
|
|
|
|
|
+ if (operationType == null) {
|
|
|
|
|
+ return "操作";
|
|
|
|
|
+ }
|
|
|
|
|
+ if (operationType.contains("新增")) {
|
|
|
|
|
+ return "新增";
|
|
|
|
|
+ }
|
|
|
|
|
+ if (operationType.contains("修改")) {
|
|
|
|
|
+ return "修改";
|
|
|
|
|
+ }
|
|
|
|
|
+ if (operationType.contains("删除")) {
|
|
|
|
|
+ return "删除";
|
|
|
|
|
+ }
|
|
|
|
|
+ return operationType;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|